[Back to NETWORK SWAG index]  [Back to Main SWAG index]  [Original]

{
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.

[Back to NETWORK SWAG index]  [Back to Main SWAG index]  [Original]