{ From: greg.miller@shivasys.com >Could you tell me where to get the ipx units? I'll post the IPX source here again. (Perhaps this should go into the FAQ? :) There seems to be a big demand for this unit. { ipx - IPX communication protocol primitives. } unit ipx; interface uses DOS; type MessageStr = String; IPX_REGS = Registers; Byte4 = array[0..3] of byte; NetworkNumber = Byte4; NetworkNode = array[0..5] of byte; ECB = record link_address : Pointer; event_service_routine : Pointer; in_use : byte; completion_code : byte; socket_number : word; ipx_workspace : Byte4; driver_workspace : array[0..11] of byte; immediate_address : NetworkNode; fragment_count : word; fragment : array [0..1] of record address : pointer; length : word; end; end; IPXHEADER = record checksum : word; length : word; transport_control : byte; packet_type : byte; dest_network_number : NetworkNumber; dest_network_node : NetworkNode; dest_network_socket : word; source_network_number : NetworkNumber; source_network_node : NetworkNode; source_network_socket : word; end; { ZeroEcb - store zeros in all ecb fields. Pre: e is an ECB. Post: e is fully zeroed. } procedure ZeroEcb( var e : ECB ); { ZeroHeader - Store zeros in all header fields. Pre: h is an IPXHEADER. Post: h is fulled zeroed. } procedure ZeroHeader( var h : IPXHEADER ); { Get1stConnectionNumber - Return first connection number for user name Pre: username is valid Novell user name Post: Returns first connection number username is logged on or 0 if not logged on. } function Get1stConnectionNumber( username : string ) : word; { GetInternetAddress - Get the network:node address for a connection. Pre: connection_number is valid for a logged on user. Post: network_number is valid number of network. physical_node is valid station node. } function GetInternetAddress( connection_number : byte; var network_number : NetworkNumber; {hi:lo} var physical_node : NetworkNode ) : integer; { IPXSPXNotLoaded - Executed when ipxspx called but not loaded. Pre: IPX not loaded. Post: Execution aborted. } procedure IPXSPXNotLoaded(var NovRegs : Registers); { IPXInstalled - Determine if IPX is installed on workstation. Pre: Either IPX is or is not installed. Post: If IPX installed initialize global IPXLocation to IPX entry point and return TRUE. Otherwise initialize global IPXLocation to IPXSPXNotLoaded entry point and return FALSE. } function IPXInstalled : Boolean; { IPXSPX - Call ipxspx at address in IPXLocation. Pre: IPXInstalled has been called. IPX is installed and NovRegs assigned IPX or SPX function and parameter values. Not checking is done. Post: IPX or SPX function is called. NovRegs assigned by call. } procedure IPXSPX(var NovRegs:Registers); { IPXRelinquishControl - Give ipx momentary control of CPU. Pre: IPX loaded. Post: IPX execution done. } procedure IPXRelinquishControl; { IPXCancelEvent - Cancels pending event associated with ECB. Pre: e is valid ECB. Post: 00 - Success. F9 - ECB cannot be canceled. FF - ECB not in use. } function IPXCancelEvent( var e : ECB ) : byte; { IPXDisconnectFromTarget - Notify listening node that communications woth specified socket are being terminated. Pre: number:node:socket are valid. Post: Node notified. } procedure IPXDisconnectFromTarget( network_number : NetWorkNumber; network_node : NetWorkNode; network_socket : word ); { IPXScheduleEvent - Schedule processing of ECB after timer ticks. Pre: ticks is number of 18.2 per second ticks. e is a valid ECB at the time processing occurs. Post: e is processed after timer ticks. } procedure IPXScheduleEvent( ticks : word; var e : ECB ); { IPXOpenSocket - Open an application socket. Pre: socket to use (BBA-7FFF). All assumed short-lived. Post: 00 - Success. FE - Socket table full. FF - Socket already open. } function IPXOpenSocket( socket : word ) : byte; { IPXCloseSocket - Close socket. No harm if already closed. Pre: socket to close. Post: socket is closed. } procedure IPXCloseSocket( socket : word ); { IPXListenForPacket - Submit an ECB for use when packet received. Must have ECB available when packet received by IPX. Pre: e storage is available when ECB processed by IPX. e.socket_number opened. e.event_svc-routine valid routine or NULL. e.fragment_count normally 2. e.fragment[0].address to IPX Header buffer. e.fragment[0].length = 30. e.fragment[1].address to data area <=546 bytes long. e.fragment[1].length = length of data area. Post: If socket opened, e is added to pool and return TRUE. Otherwise return FALSE. } function IPXListenForPacket( var e : ECB ) : Boolean; { IPXSendPacket - Send packet using given ECB. Pre: e storage is available when ECB processed by IPX. e.socket_number opened. e.event_svc-routine valid routine or NULL. e.immediate_address is address of destination workstation. e.fragment_count normally 2. e.fragment[0].address to IPX Header buffer. e.fragment[0].length = 30. e.fragment[1].address to data area <=546 bytes long. e.fragment[1].length = length of data area. Post: e.completion_code of: 00 - Message sent. FC - Event canceled. FD - Bad packet. } { IPXGetLocalTarget - Get the bridge address (or node if not bridged) for network:node address. Pre: dest_network - network number of workstation. dest_node - network node of workstation. dest_socket - network socket of workstation. Post: bridge_address is routing information used by IPXSendPacket. Return 00 - Success. FA - No path to destination. } function IPXGetLocalTarget( var dest_network : NetworkNumber; var dest_node : NetworkNode; dest_socket : word; var bridge_address : NetworkNode ) : byte; { IPXGetIntervalMarker - Return time marker measured in 18.2/sec ticks. Pre: None. Post: Return time marker. } function IPXGetIntervalMarker : word; { IPXSend - Send a packet to network:node:socket using send_ecb and send_header. send_ecb/send_header should be defined outside of IPXSend as both may be in use by ipx after IPXSend completes, releasing any local variables. Pre: dest_network - network number of destination. dest_node - network node of destination. dest_socket - socket of destination. packet_ptr - pointer to send packet. packet_len - length of send packet send_ecb - ECB to use for sending. send_header - IPXHEADER to use for sending. send_socket - socket to use for sending. Post: If destination reachable, packet is sent. } procedure IPXSend( var dest_network : NetworkNumber; var dest_node : NetworkNode; dest_socket : word; { hi:lo } packet_ptr : Pointer; packet_len : integer; var send_ecb : ECB; var send_header : IPXHEADER; send_socket : word ); { IPXReceive - Submit an ECB/header and storage buffer for a received message. Pre: receive_ecb - ECB allocated for recieving. receive_header - IPXHEADER allocated for receiving. receive_socket - socket to receive on. Post: message - area allocated for received message holds data. message_size - size of message area in bytes. } procedure IPXReceive( var receive_ecb : ECB; var receive_header : IPXHEADER; receive_socket : word; message : Pointer; message_size : word ); { IPXReceivedFrame - Returns TRUE if message frame received in ECB. Pre: receive_ecb - ECB allocated for recieving. Post: Returns TRUE if message frame received in ECB. } function IPXReceivedFrame( receive_ecb : ECB ) : Boolean; {_________________________________________________________________________} implementation type REQUESTBUFFER = record dest_network_number : NetWorkNumber; dest_network_node : NetworkNode; dest_network_socket : word; end; REPLYBUFFER = record node_address : NetworkNode; end; var IPXLocation : Pointer; { Address of ipx } { abort - Display message and halt. Pre: message is a string. } procedure abort( message : string ); begin writeln( message ); Halt(1); end; {$F+} { Get1stConnectionNumber - Return first connection number for user name Pre: username is valid Novell user name Post: Returns first connection number username is logged on or 0 if not logged on. } function Get1stConnectionNumber( username : string ) : word; var NovRegs : Registers; Request : record len : Word; buffer_type : Byte; object_type : Word; name : string[47]; end; Reply : record len : Word; number_connections : byte; connection_num : array[0..99] of byte; end; begin with Request do begin len := 51; buffer_type := $15; object_type := $0100; name := username; end; Reply.len := 101; { Maximum number of user connections } with NovRegs do begin AH := $E3; DS := Seg(Request); {DS:SI points to request} SI := Ofs(Request); ES := Seg(Reply); {ES:DI points to reply} DI := Ofs(Reply); MsDos(NovRegs); if (Al <> 0) or (Reply.number_connections = 0) then Get1stConnectionNumber := 0 else Get1stConnectionNumber := Reply.connection_num[0]; end; end; { GetInternetAddress - Get the network:node address for a connection. Pre: connection_number is valid for a logged on user. Post: network_number is valid number of network. physical_node is valid station node. } function GetInternetAddress( connection_number : byte; var network_number : NetworkNumber; {hi:lo} var physical_node : NetworkNode ) : integer; var NovRegs : Registers; Request : record len : word; buffer_type : byte; connection_number : byte; end; Reply : record len : word; network_number : NetworkNumber; physical_node : NetworkNode; server_socket : word; end; begin with Request do begin len := 2; buffer_type := $13; end; Request.connection_number := connection_number; Reply.len := 12; with NovRegs do begin AH := $E3; DS := Seg(Request); {DS:SI points to request} SI := Ofs(Request); ES := Seg(Reply); {ES:DI points to reply} DI := Ofs(Reply); MsDos(NovRegs); Ah := 0; GetInternetAddress := Ax; end; network_number := Reply.network_number; physical_node := Reply.physical_node; end; { IPXSPXNotLoaded - Executed when ipxspx called but not loaded. Pre: IPX not loaded. Post: Execution aborted. } procedure IPXSPXNotLoaded(var NovRegs : Registers); begin abort('IPX not loaded'); end; { ZeroEcb - store zeros in all ecb fields. Pre: e is an ECB. Post: e is fully zeroed. } procedure ZeroEcb( var e : ECB ); var i : byte; begin with e do begin link_address := Ptr(0,0); event_service_routine := Ptr(0,0); in_use := 0; completion_code := 0; socket_number := 0; for i := 0 to 3 do ipx_workspace[i] := 0; for i := 0 to 11 do driver_workspace[i] := 0; for i := 0 to 5 do immediate_address[i] := 0; fragment_count := 0; for i := 0 to 1 do begin fragment[i].address := Ptr(0,0); fragment[i].length := 0; end; end; end; { ZeroHeader - Store zeros in all header fields. Pre: h is an IPXHEADER. Post: h is fulled zeroed. } procedure ZeroHeader( var h : IPXHEADER ); var i : byte; begin with h do begin checksum := 0; length := 0; transport_control := 0; packet_type := 0; for i := 0 to 3 do dest_network_number[i] := 0; for i := 0 to 5 do dest_network_node[i] := 0; dest_network_socket := 0; for i := 0 to 3 do source_network_number[i] := 0; for i := 0 to 5 do source_network_node[i] := 0; source_network_socket := 0; end; end; { IPXInstalled - Determine if IPX is installed on workstation. Pre: Either IPX is or is not installed. Post: If IPX installed initialize global IPXLocation to IPX entry point and return TRUE. Otherwise initialize global IPXLocation to IPXSPXNotLoaded entry point and return FALSE. } function IPXInstalled : Boolean; var NovRegs : IPX_REGS; begin with NovRegs do begin AX := $7A00; {func 7Ah of int 2Fh is used to detect IPX} Intr($2F,NovRegs); if AL = $FF then begin {if AL is FFh then IPX is loaded and available} IPXInstalled := TRUE; IPXLocation := Ptr(ES,DI); {pointer to IPX entry point in ES:DI} end else begin IPXInstalled := FALSE; {no IPX installed} IPXLocation := @IPXSPXNotLoaded; end; end; end; { IPXSPX - Call ipxspx at address in IPXLocation. Pre: IPXInstalled has been called. IPX is installed and NovRegs assigned IPX or SPX function and parameter values. Not checking is done. Post: IPX or SPX function is called. NovRegs assigned by call. } procedure IPXSPX(var NovRegs:Registers); var Ax_, Bx_, Dx_, Di_, Si_, Es_ : word; begin with NovRegs do begin { Assign simple variables record field values } Ax_ := Ax; Bx_ := Bx; Dx_ := Dx; Di_ := Di; Si_ := Si; Es_ := Es; end; asm { Assembler instructions. } mov Ax, Ax_ { Initialize CPU registers.} mov Bx, Bx_ mov Dx, Dx_ mov Di, Di_ mov Si, Si_ mov Es, Es_ push Bp call dword ptr IPXLocation { Call IPX via address at IPXLocation. } pop Bp mov Ax_, Ax mov Dx_, Dx end; NovRegs.Ax := Ax_; { Return register values to caller } NovRegs.Dx := Dx_; end; { IPXRelinquishControl - Give ipx momentary control of CPU. Pre: IPX loaded. Post: IPX execution done. } procedure IPXRelinquishControl; var NovRegs : IPX_REGS; begin with NovRegs do begin Bx := $0a; IPXSPX(NovRegs); end end; { IPXCancelEvent - Cancels pending event associated with ECB. Pre: e is valid ECB. Post: 00 - Success. F9 - ECB cannot be canceled. FF - ECB not in use. } function IPXCancelEvent( var e : ECB ) : byte; var NovRegs : IPX_REGS; begin with NovRegs do begin Bx := $06; ES := Seg(e); {ES:SI points to ecb} SI := Ofs(e); IPXSPX(NovRegs); IPXCancelEvent := AL; end end; { IPXDisconnectFromTarget - Notify listening node that communications woth specified socket are being terminated. Pre: number:node:socket are valid. Post: Node notified. } procedure IPXDisconnectFromTarget( network_number : NetWorkNumber; network_node : NetWorkNode; network_socket : word ); var NovRegs : IPX_REGS; request_buffer : REQUESTBUFFER; begin with request_buffer do begin dest_network_number := network_number; dest_network_node := network_node; dest_network_socket := network_socket; end; with NovRegs do begin Bx := $0B; ES := Seg(request_buffer); {ES:SI points to ecb} SI := Ofs(request_buffer); IPXSPX(NovRegs); end end; { IPXScheduleEvent - Schedule processing of ECB after timer ticks. Pre: ticks is number of 18.2 per second ticks. e is a valid ECB at the time processing occurs. Post: e is processed after timer ticks. } procedure IPXScheduleEvent( ticks : word; var e : ECB ); var NovRegs : IPX_REGS; begin with NovRegs do begin Bx := $05; Ax := ticks; ES := Seg(e); SI := Ofs(e); IPXSPX(NovRegs); end; end; { IPXOpenSocket - Open an application socket. Pre: socket to use (BBA-7FFF). All assumed short-lived. Post: 00 - Success. FE - Socket table full. FF - Socket already open. } function IPXOpenSocket( socket : word ) : byte; var NovRegs : IPX_REGS; begin with NovRegs do begin Dx := socket; Bx := 0; Al := 0; IPXSPX(NovRegs); Ah := 0; IPXOpenSocket := Ax; end end; { IPXCloseSocket - Close socket. No harm if already closed. Pre: socket to close. Post: socket is closed. } procedure IPXCloseSocket( socket : word ); var NovRegs : IPX_REGS; begin with NovRegs do begin Dx := socket; Bx := $0001; IPXSPX(NovRegs); end end; { IPXListenForPacket - Submit an ECB for use when packet received. Must have ECB available when packet received by IPX. Pre: e storage is available when ECB processed by IPX. e.socket_number opened. e.event_svc-routine valid routine or NULL. e.fragment_count normally 2. e.fragment[0].address to IPX Header buffer. e.fragment[0].length = 30. e.fragment[1].address to data area <=546 bytes long. e.fragment[1].length = length of data area. Post: If socket opened, e is added to pool and return TRUE. Otherwise return FALSE. } function IPXListenForPacket( var e : ECB ) : Boolean; var NovRegs : IPX_REGS; begin with NovRegs do begin BX := $0004; ES := Seg(e); {ES:SI points to ecb} SI := Ofs(e); IPXSPX(NovRegs); IPXListenForPacket := Al = 00; end end; { IPXSendPacket - Send packet using given ECB. Pre: e storage is available when ECB processed by IPX. e.socket_number opened. e.event_svc-routine valid routine or NULL. e.immediate_address is address of destination workstation. e.fragment_count normally 2. e.fragment[0].address to IPX Header buffer. e.fragment[0].length = 30. e.fragment[1].address to data area <=546 bytes long. e.fragment[1].length = length of data area. Post: e.completion_code of: 00 - Message sent. FC - Event canceled. FD - Bad packet. } procedure IPXSendPacket( var e: ECB ); var NovRegs : IPX_REGS; begin with NovRegs do begin ES := Seg(e); {ES:SI points to ecb} SI := Ofs(e); BX := $0003; IPXSPX(NovRegs); end end; { IPXGetLocalTarget - Get the bridge address (or node if not bridged) for network:node address. Pre: dest_network - network number of workstation. dest_node - network node of workstation. dest_socket - network socket of workstation. Post: bridge_address is routing information used by IPXSendPacket. Return 00 - Success. FA - No path to destination. } function IPXGetLocalTarget( var dest_network : NetworkNumber; var dest_node : NetworkNode; dest_socket : word; var bridge_address : NetworkNode ) : byte; var NovRegs : Registers; Request : record network_number : NetworkNumber; physical_node : NetworkNode; socket : word; end; Reply : record local_target : NetworkNode; end; begin with Request do begin network_number := dest_network; physical_node := dest_node; socket := dest_socket; end; with NovRegs do begin Es := Seg(Request); Si := Ofs(Request); Di := Ofs(Reply); Bx := $0002; IPXSPX(NovRegs); Ah := 0; IPXGetLocalTarget := Ax; bridge_address := Reply.local_target; end end; { IPXGetIntervalMarker - Return time marker measured in 18.2/sec ticks. Pre: None. Post: Return time marker. } function IPXGetIntervalMarker : word; var NovRegs : Registers; begin with NovRegs do begin Bx := $0008; IPXSPX(NovRegs); IPXGetIntervalMarker := Ax; end end; { IPXSend - Send a packet to network:node:socket using send_ecb and send_header. send_ecb/send_header should be defined outside of IPXSend as both may be in use by ipx after IPXSend completes, releasing any local variables. Pre: dest_network - network number of destination. dest_node - network node of destination. dest_socket - socket of destination. packet_ptr - pointer to send packet. packet_len - length of send packet send_ecb - ECB to use for sending. send_header - IPXHEADER to use for sending. send_socket - socket to use for sending. Post: If destination reachable, packet is sent. } procedure IPXSend( var dest_network : NetworkNumber; var dest_node : NetworkNode; dest_socket : word; { hi:lo } packet_ptr : Pointer; packet_len : integer; var send_ecb : ECB; var send_header : IPXHEADER; send_socket : word ); begin ZeroEcb(send_ecb); ZeroHeader(send_header); send_ecb.socket_number := send_socket; { Socket used for sending } if IPXGetLocalTarget( dest_network, dest_node, dest_socket, send_ecb.immediate_address ) = 0 then begin with send_ecb do begin fragment_count := 2; fragment[0].address := @send_header; fragment[0].length := sizeof(IPXHEADER); fragment[1].address := packet_ptr; fragment[1].length := packet_len; end; with send_header do begin packet_type := 4; dest_network_number := dest_network; dest_network_node := dest_node; dest_network_socket := dest_socket; end; IPXSendPacket( send_ecb ); end; end; { IPXReceive - Submit an ECB/header and storage buffer for a received message. Pre: receive_ecb - ECB allocated for recieving. receive_header - IPXHEADER allocated for receiving. receive_socket - socket to receive on. Post: message - area allocated for received message holds data. message_size - size of message area in bytes. } procedure IPXReceive( var receive_ecb : ECB; var receive_header : IPXHEADER; receive_socket : word; message : Pointer; message_size : word ); begin ZeroEcb(receive_ecb); ZeroHeader(receive_header); with receive_ecb do begin socket_number := receive_socket; { Socket used for receiving } fragment_count := 2; fragment[0].address := @receive_header; fragment[0].length := sizeof(IPXHEADER); fragment[1].address := message; fragment[1].length := message_size; end; if not IPXListenForPacket( receive_ecb ) then abort('IPX Error - Failure initializing.'); IPXRelinquishControl; { Give ipx opportunity to process } end; { IPXReceivedFrame - Returns TRUE if message frame received in ECB. Pre: receive_ecb - ECB allocated for recieving. Post: Returns TRUE if message frame received in ECB. } function IPXReceivedFrame( receive_ecb : ECB ) : Boolean; begin IPXReceivedFrame := (receive_ecb.completion_code = 0) and (receive_ecb.in_use = 0); end; begin end.