MS-DOS and DR-DOS

Under DOS environments, each license system provider is implemented as Terminate-and-Stay-Resident (TSR) module. DOS applications statically link to the library LSAPI.LIB. The calling convention for the licensing functions is C language. The LSAPI.LIB library is responsible for coordinating the interaction between the application and the underlying provider TSRs. The LSAPI.LIB itself is not a license system service provider. Applications do not interact directly with the provider TSRs (but may choose to do so).

A license system provider TSR attaches itself to the interrupt 2Fh vector and watches for AH=70h. Upon a match, the TSR further examines the CX register to see if the slot number matches.

Great care should be taken in developing the TSR. The memory space available to MS-DOS applications is very limited. The TSR should be a small and as efficient as possible, discarding initialization code, etc. Provider developers might even consider storing the majority of the provider code into Expanded (EMS) Memory, and only have a small stub as the TSR.

Data Structures

All strings pointed to below are NULL-terminated. The following defines the SLSREQUEST structure:

SLSREQUEST struct
Status DD ? ; [out] status code
LicenseHandle DD ? ; [out] handle identifying context
PublisherName DD ? ; pointer to Publisher string
ProductName DD ? ; pointer to Product string
Version DD ? ; pointer to Version string
TotUnitsReserved DD ? ; total units to be reserved
LogComment DD ? ; pointer to comment string
Challenge DD ? ; pointer to SLSCHALLENGE structure
ChallengeResponse DD ? ; pointer to the challenge response
TotUnitsGranted DD ? ; [out] total units actually reserved
SLSREQUEST ENDS

The following defines the SLSCHALLENGE structure:

SLSCHALLENGE struct
Protocol DD ? ; specifies the protocol
Size DD ? ; size of ChallengeData structure
ChallengeData LABEL BYTE
SLSCHALLENGE ENDS

The following defines the SLSCHALLENGEDATA structure:

SLSCHALLENGEDATA struct
ChallengedSecret DD ? ; secret to be challenged (1-4)
Random DD ? ; random 32-bit value
MessageDigest DB 16 DUP ? ; message digest structure
SLSCHALLENGEDATA ENDS

The following defines the SLSRELEASE structure:

SLSRELEASE struct
Status DD ? ; [out] Result of the operation

LicenseHandle DD ? ; handle identifying license context
TotUnitsConsumed DD ? ; total units consumed
LogComment DD ? ; pointer to comment string
SLSRELEASE ENDS

The following defines the SLSFREEHANDLE structure:

SLSFREEHANDLE struct

LicenseHandle DD ? ; The license handle to free

SLSFREEHANDLE ENDS

The following defines the SLSUPDATE structure:

SLSUPDATE struct
Status DD ? ; [out] status code
hLicenseHandle DD ? ; handle identifying license context
TotUnitsConsumed DD ? ; total units consumed
TotUnitsReserved DD ? ; total units to be reserved
LogComment DD ? ; pointer to comment string
Challenge DD ? ; pointer to SLSCHALLENGE structure
ChallengeResponse DD ? ; pointer to the challenge response
TotUnitsGranted DD ? ; [out] total units actually reserved
SLSUPDATE ENDS

The following defines the SLSGETMESSAGE structure:

SLSGETMESSAGE struct
Status DD ? ; [returned] status code
hLicenseHandle DD ? ; handle identifying license context
Value DD ? ; indicates the error code
BufferSize DD ? ; buffer size in bytes
Buffer DB BufferSize DUP (?)
; [returned] message string
SLSGETMESSAGE ENDS

The following defines the SLSQUERY structure:

SLSQUERY struct
Return DD ? ; [returned] status code
LicenseHandle DD ? ; handle identifying license context
InfoIndex DD ? ; information index
InfoBufferSize DD ? ; buffer size in bytes
InfoBuffer DB InfoBufferSize DUP (?)

; [returned] requested information

ActualBufferSize DD ? ; [out] # of bytes returned in InfoBuffer
SLSQUERY ENDS

The following defines the SLSENUMPROVIDERS structure:

SLSENUMPROVIDERS struct

Status DD ? ;[out] returned status

Index DD ? ; the index of the provider

Buffer DD ? ; a pointer to the buffer

SLSENUMPROVIDERS ENDS

Interrupt 2Fh Handler

The interrupt 2Fh is the general purpose multiplexed interface used by network software, the operating system, and various system utility programs. The interface is designed so that functions can be hooked in a chain-like fashion. Each interrupt 2Fh handler is expected to implement an installation check function. The AH register contains the multiplex channel, and the AL register contains the specific function code for that handler. Function code AL=00h is the installation check function. If the handler is installed, it must return the value FFh in the AL register. The last handler in the interrupt 2Fh chain (maintained by DOS) will always return a value of 00h in the AL register, indicating that the desired multiplex channel is not installed.

Every Multiplex interrupt handler uses a specific multiplex number which must be given in the AH register. For LSAPI provider TSRs, the value for AH is 70h. Handlers are chained into the 2Fh interrupt vector and discriminate based on the value in AH. The specific function to be performed by the selected handler is placed into the AL register. Other parameters are placed in the remaining registers as needed. Multiplex numbers AH=00h through AH=7Fh are reserved for the operating system, while AH=0B8h through AH=0BFh are reserved for network use. Other applications should use multiplex numbers AH=80h through AH=0FFh.

The following incomplete sample code segments illustrate how the interrupt 2Fh vector is "chained" into.

;

; The following segment of code is used to hook the INT 2Fh vector.;

;

...

CALL NEAR PTR FindSlot ; find an unused CX value

JC noslots ; If none available, error...

CALL NEAR PTR SetVector ; take over INT 2Fh vector

OR AX, AX ; did set vector succeed?

JNZ setfailed ; YES: ...

...

...

; FindSlot()

; On Entry: Nothing

; On Exit: CX = next unused index slot

;;

FindSlot Proc NEAR

ASSUME CS:CODE, DS:DATA, ES:NOTHING

XOR CX, CX ; service provider index

fs_loop: MOV AX, 7000h ; installation check function

INT 2Fh ; Do install check

OR AL, AL ; Is AL=00h?

JZ fs_done ; YES: CX is our provider index

INC CX ; NO: Try next CX value

CMP CL, 0Fh ; Up to 15 providers?

JB fs_loop ; NO: keep trying...

MOV CX, 0FFFFh ; YES: return error (wow!)

STC ;

fs_done: RET ;;

FindSlot ENDP

; SetVector()

; On Entry: CX = provider slot #

; On Exit: INT 2Fh vector set to CODE:Handler

; Original vector saved in CODE:OldHandler

; Slot # (from CX) saved in CODE:myslot

; ES:BX set to original vector.

; Other registers undetermined.

; AX = 0000h on success, else error code

;;

SetVector PROC NEAR

ASSUME CS:CODE, DS:NOTHING, ES:NOTHING

MOV AX, 352Fh ; Get INT 2Fh vector

INT 21h ; into ES:BX...

MOV WORD PTR CS:OldHandler, BX ; save segment

MOV WORD PTR CS:OldHandler+2, ES ;

MOV WORD PTR CS:myslot, CX ; save my slot #

PUSH DS ; set up DS:SI

MOV DS, SEG CODE ;

ASSUME DS:CODE ;

MOV DX, OFFSET CODE:Handler ; DS:SI --> new handler

MOV AL, 2Fh ; interrupt to set

MOV AH, 25h ; DOS set vector

INT 21h ; Set it!!

POP DS ; get back DS=DATA

RET ;;

SetVector ENDP

; Handler()

; This routine handles the interrupt 2Fh vector.

; On Entry: AH = multiplex channel

; AL = function code

; CX = slot # (if AH=70h)

; On Exit: As appropriate for function.

;;

Handler PROC FAR

ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING

CMP AH, 70h ; LSAPI function?

JZ is_lsapi ; YES: look further

chain: JMP DWORD PTR CS:OldHandler ; NO: chain to next handler

is_lsapi: CMP CX, CS:myslot ; request for my slot #?

JNZ chain ; NO: chain to next

; looks like we have a function for us

; lets check it out

CMP AL, 00h ; Install check?

JZ instchk ; YES: handle it

CMP AL, 05h ; function in range?

JA badfunc ; NO: return failure

; we get here for functions AL=02h through AL=05h

; PLACE CODE HERE FOR OTHER FUNCTIONS

; SAVE REGISTERS, ETC.

; THIS MIGHT BE A GOOD PLACE FOR A JUMP TABLE

...

...

instchk: MOV AL, 0FFh ; indicate we're installed

IRET

badfunc: MOV AX, 0001h ; invalid function code

IRET

...

Handler ENDP

; CODE segment data variables

OldHandler DD ? ; address of old INT 2Fh handler

myslot DW ? ; provider index #

Contact Microsoft's or Novell's Developer Relations Group for further information.

INT 2Fh, AX=7000h -- Installation Check

Before a service provider hooks into the INT 2Fh chain, it must look for a free index slot. This is accomplished by placing an index value into the CX register and issuing an installation check (detailed below). If a particular slot is in use, the AL register will be returned with 0xFF. Otherwise, the AL register will remain unchanged. The provider issues the installation check call, incrementing the index value (starting at zero) each time, until it locates a free slot.

Once a free index value has been identified, the provider may hook into the INT 2Fh interrupt chain and go TSR. Thereafter, the TSR must respond to future installation checks when CX contains its index value.

On Entry:

AX

7000h

CX

Index of license service provider TSR. This may range from 0x0000 to 0x001F, inclusive. The first provider TSR should use 0x0000; the second 0x0001; the third 0x0002; and so on.

On Exit:

AL

Status: 0x00=not-installed; 0xFF=installed (slot in use)


This document does not detail the techniques required to properly chain a software interrupt. Developers should consult the MS-DOS Programmer's Reference or the MS-DOS Encyclopedia for further information.

INT 2Fh, AX=7001h -- Request License

On Entry:

AX

7001h

CX

Index of license system service provider

DS:DX

Pointer to SLSREQUEST structure (see Data Structures on page 31)

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other=provider error

ES:BX

Contains the provider-specific handle identifying the license context


INT 2Fh, AX=7002h -- Release License

On Entry:

AX

7002h

CX

Index of license system service provider

DS:DX

Pointer to SLSRELEASE structure (see Data Structures on page 31)

ES:BX

Handle identifying the license context

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other=provider error


INT 2Fh, AX=7003h -- Update

On Entry:

AX

7003h

CX

Index of license service provider.

DS:DX

Pointer to SLSUPDATE structure (see Data Structures on page 31)

ES:BX

Handle identifying the license context

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other=provider error


INT 2Fh, AX=7004h -- Get Message

On Entry:

AX

7004h

CX

Index of license service provider TSR..

DS:DX

Pointer to SLSGETMESSAGE structure (see Data Structures on page 31)

ES:BX

Handle identifying the license context

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other=provider error


INT 2Fh, AX=7005h -- Query License

On Entry:

AX

7005h

CX

Index of license service provider TSR..

DS:DX

Pointer to SLSQUERY structure (see Data Structures on page 31)

ES:BX

Handle identifying the license context

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other=provider error


INT 2Fh, AX=7006h -- Free Licensing Handle

On Entry:

AX

7006h

CX

Index of license service provider TSR..

DS:DX

Pointer to SLSFREEHANDLE structure (see Data Structures on page 32)

ES:BX

Handle identifying the license context

On Exit:

No returned values, always successful.


INT 2Fh, AX=7007h -- Enumerate License Providers

On Entry:

AX

7007h

CX

Index of license service provider TSR..

DS:DX

Pointer to SLSENUMPROVIDERS structure (see Data Structures on page 32)

On Exit:

AX

Status: 0x0000=complete (see structure for full status code); any other = provider error