{A PCTCP interface. Send any questions or corrections to: gmiller@ius.indiana.edu or greg.miller@shivasys.com or post them publically to comp.lang.pascal NOTE: The PCTCP interface must be installed on the default (INT 61h) interrupt handler.} unit pctcp; interface uses dos; const tcp_error:array[0..28] of string = ('No Error','Specified Protocol Socket is Already In Use', 'MS-DOS Error. High Portion of ERROR Contains Error Code', 'Out of Memory Error','Not a Network Descriptor', 'Invalid Operation of Descriptor', 'Illegal or Corrupted Packet','No Host Bound to Specified Connection', 'Unable to Open File','Network is Unreachable', 'Host is Unreachable (see subcodes)', 'Protocol is Unreachable','Port is Unreachable','Time Out Interval Reached', 'Unable to Resolve Hostname','No Name Servers Configured', 'Bad Reply From Server (sub 1 = host unreachable', 'Bad Format for IP Address or IP Address is Zero', 'Bad Argument','Remote Host Closed its Side of Connection)', 'Connection has Been Reset','Call Would Block the Caller', 'Insufficient Resources to do Operation', 'Could not Allocate Netword Descriptor', 'Invalid Kernel Call','Unable to Broadcast', 'Operation Illegal Because Connection not Established', 'Kernel Busy','ICMP Message was Received (see subcodes)'); {Constants for general errors. Low portion contains the main error code, while the high portion contains the sub error code (if any).} net_noerr = $0; {no error} net_err_inuse = $1; {specified protocol socket is already in use} net_dos_err = $2; {MS-DOS error. High portion of ERROR contains error code} net_err_nomem = $3; {out of memory error} net_err_notnetconn = $4; {not a network descriptor} net_err_illegalop = $5; {invalid operation of descriptor} net_err_badpkt = $6; {illegal or corrupted packet} net_err_nohost = $7; {no host bound to specified connection} net_err_cantopen = $8; {unable to open file} net_err_net_unreachable = $9; {network is unreachable} net_err_host_unreachable = $a; {host is unreachable (see subcodes)} net_err_prot_unreachable = $b; {protocol is unreachable} net_err_port_unreachable = $c; {port is unreachable} net_err_timeout = $d; {time out interval reached} net_err_hostunknown = $e; {unable to resolve hostname} net_err_noservers = $f; {no name servers configured} net_err_server_err = $10; {bad reply from server (sub 1 = host unreachable} net_err_badformat = $11; {bad format for IP address or IP address is zero} net_err_badarg = $12; {bad argument} net_err_eof = $13; {eof reached (remote host closed its side of connection)} net_err_reset = $14; {connection has been reset} net_err_wouldblock = $15; {call would block the caller} net_err_unbound = $16; {insufficient resources to do operation} net_err_nodesc = $17; {could not allocate netword descriptor} net_err_badsyscall = $18; {invalid kernel call} net_err_cantbraodcast = $19; {unable to broadcast} net_err_notestab = $1a; {operation illegal because connection not established} net_err_kernelbusy = $1b; {kernel busy} net_err_icmpmesg = $1c; {ICMP message was received (see subcodes)} host_unreacable_error:array[0..6] of string = ('No error','Host_unreachable','Address Resolution Failed', 'Hardware Failure','Link Failure','Routing Failure','Gateway Down'); {Constants for subcode of net_err_host_unreachable} no_err = $0; host_unreachable = $1; arp_failed = $2; hardware_failure = $3; link_failure = $4; rout_failure = $5; gateway_down = $6; icmpmsg_error:array[7..13] of string = ('Unrecognized','Cannot Fragment packet','Source Routing Failure', 'Source Quench','Time limit Exceeded','Parameter Problem', 'Administration Problem'); {Constants for subcode net_err_icmpmsg} unrecognized = $7; cant_fragment = $8; srcr_fail = $9; source_quench = $a; time_exceeded = $b; parameter_problem = $c; admin_prohib = $d; {Constants for protocol type} protocol_raw_net = 1; protocol_raw_ip = 2; protocol_datagram = 3; {UDP protocol} protocol_stream = 4; {TCP protocol} protocol_raw_icmp = 5; {Constants for send options} urgent = 1; {Signal urgent data on destenation} rerout = 8; {Attempt rerouting on non-stream calls if first attempt fails} push = 16; {send data with push flag (Nagle's algorithm is not overridden} no_trunc = 32; {abort send if datagram must be truncated} no_block = 64; {abort send if call must be blocked} broadcast = 128; {Constants for receive options} no_remove = 2; {data is to be copied from the queue but not removed} no_copy = 4; {data is to be removed from the queue but not copied} {no_trunc = 32; abort if datagram needs to be truncated (same as send opt} {no_block = 64; abort if call must be blocked (same as send options} type dword = record a,b,c,d: byte; end; type addr = record IP_addr: dword; remote_socket: word; local_socket: word; protocol: byte; end; type configuration_information_record = record max_tcp_connections: byte; max_udp_connections: byte; max_ip_connections: byte; max_raw_net_connections: byte; current_tcp_connections: byte; current_udp_connections: byte; current_ip_connections: byte; current_raw_net_connections: byte; active_local_descriptors: word; active_global_descriptors: word; max_header_size: byte; max_trailer_size: byte; sizeof_packet_buffer: word; network_interfaces_attatched: word; ellapsed_time: dword; {Time in milliseconds since begin of kernel} ip_broadcast_adr: dword; end; type interface_statistics_record = record interface_class:word; type_of_interface:word; interface_number:word; ip_addr:dword; subnet_mask:dword; interface_status:word; {0001 if interface is up} packets_received:longint; packets_sent:longint; receive_errors:longint; send_errors:longint; local_addr_len:word; {Length of local network address} net_addr:longint; {pointer to local network address} end; type TCP_connection_stats = record unused1: array[1..16] of byte; bytes_sent:longint; bytes_received:longint; unused2: array[1..8] of byte; packets_sent:longint; packets_received:longint; bad_checksums:longint; window_count:longint; timeouts:longint; resets:longint; duplicate_packets:longint; retranmitions:longint; end; type IP_connection_stats = record unused1: array[1..8] of byte; IP_header_len_errors:longint; protocol_errors:longint; dup_frags_received:longint; bad_frags_received:longint; security_errors:longint; bad_IP_addrs_received:longint; packets_sent:longint; packets_received:longint; bad_checksums:longint; IP_protocol_errors:longint; fragmentation_errors:longint; bad_dscrd_scrty_frgs:longint; fragments_received:longint; unused4:array[1..4] of byte; end; type UDP_connection_stats = record unused1:array[1..28] of byte; packets_discarded: longint; packets_sent:longint; packets_received:longint; bad_checksums:longint; port_listening_errors:longint; unused2:array[1..4] of byte; truncated_receives:longint; unused3:array[1..8] of byte; end; type ICMP_connection_stats = record TIMEEX_sent:longint; TIMEEX_received:longint; PARAMPROB_sent:longint; PARAMPROB_received:longint; redirects_received:longint; source_qnches_received:longint; echo_req_sent:longint; echo_req_received:longint; packets_sent:longint; packets_received:longint; bad_packets_received:longint; DESTUN_received:longint; packet_send_errors:longint; DESTUN_sent:longint; echo_reps_received:longint; echo_reps_sent:longint; end; type hostname = array[1..127] of char; var a:array[1..6] of char; {Returns true if PCTCP is installed on the default 61h interrupt vector.} function pctcp_installed:boolean; {Get the internet address related to a descriptor} procedure get_addr(descriptor:word; var a,b,c,d:byte; var error:word); {Get network interface statistics.} procedure net_info(descriptor:word; var r:interface_statistics_record; var error:word); {Make an allocated network descriptor global} procedure net_globalize(descriptor:word; var global_descriptor,error:word); {Release a valid allocated network descriptor} procedure net_release(descriptor:word; var error:word); {Close all non-global descriptors (globals must be closed individualy)} procedure net_releaseall; {Get statistics of TCP connection} procedure net_stat_TCP(var r:TCP_connection_stats; var error:word); {Get statistics of IP connection} procedure net_stat_IP(var r:IP_connection_stats; var error:word); {Get statistics of UDP connection} procedure net_stat_UDP(var r:UDP_connection_stats; var error:word); {Get statistics of ICMP connection} procedure net_stat_ICMP(var r:ICMP_connection_stats; var error:word); {Determine if a possible network descriptor is valid.} procedure is_netnd(descriptor:word; var error:word); {disable asynchronous handlers} procedure disable_async; {enable asynchronous handlers and return previous state} procedure enable_async(var already_enabled:boolean); {Open a network connection with descriptor. If the descriptor supplied is equal to 0xFFFF a descriptor is automatically allocated and returned in DESCRIPTOR after the connection is made.} procedure net_connect(var descriptor:word; protocol:word; addr_struct:addr; var error:word); {Close transmition side of connection. The connection remains open, but data can no longer be transmited onto the network, though data may still be read.} procedure net_eof(descriptor:word; var error:word); {Imdediatley close a connection} procedure net_abort(descriptor:word; var error:word); {Write data to the network. seg_buf and off_buf contain the segment and offset of the data to be written. Size contains the length of the data to be written. Upon return written contains the number of bytes acutally written and error contains zero if the write was successful. Written contains zero and error contains the error code if the write was unsuccessful.} procedure net_write(descriptor,size,send_options,seg_buf,off_buf:word; var written,error:word); {Read data from the network. seg_buff and off_buf contain the segment and offset of the data to be written. Size contains the maximum number of bytes to read. Upon return read contains the number of bytes actually read and error contains zero if the read was successful. Read contains zero and error contains the error code if the read was unsuccessful. seg_addr and off_addr contain the segment and offset of the addr structure for the remote to read from or 0000:0000 for any.} procedure net_read(descriptor,size,receive_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); {Read datagram from the network. seg_buff and off_buf contain the segment and offset of the data to be written. Size contains the maximum number of bytes to read. Upon return read contains the number of bytes actually read and error contains zero if the read was successful. Read contains zero and error contains the error code if the read was unsuccessful. seg_addr and off_addr contain the segment and offset of the addr structure for the remote to read from. This function is for use with only datagram or raw descriptors.} procedure net_readfrom(descriptor,size,receive_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); {Write a datagram to the network. This function differs from net_write in that the addr stucture can be used to override the IP and socket values.} procedure net_writeto(descriptor,size,send_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); {Transmit all pending data imediatley} procedure net_flush(descriptor:word; var error:word); {Set up asynchronous handler. Upon return seg_old and off_old contain the segment and offset of the old asynchronous handler.} procedure net_asynch(descriptor,event_type,seg_handler,off_handler:word; hint:dword; var seg_old,off_old,error:word); {allocates a network descriptor. Error = 0 indicates success} procedure net_getdesc(var descriptor, error:word); {Listen for incomming connections} procedure net_listen(var descriptor:word; service_type:word; var addr_struct:addr; var error:word); {Allocate a global descriptor. Error 0 indicates success.} procedure net_getglobdesc(var descriptor,error:word); {swap two network descriptors} procedure net_swap(var descriptor1,descriptor2,error:word); {Reads configuration information from PCTCP driver.} procedure get_configuration_information (var r:configuration_information_record); {Returns the host IP address in network byte order given the hostname. The hostname is resolved by numerical, host table, DNS or IEN116 methods respectively.} procedure nm_res_name(var a,b,c,d:byte;{Host IP address in Network Byte Order} length:word; {length of host name} host:hostname;{ASCIZ character array hostname} var error:word); {Sends a specified amount of random data to the specified host which should be echoed back. 0 in error indicates a success.} procedure icmp_ping(a,b,c,d:byte; {Host IP address in Network Byte Order} len:byte; {Length of data to send} var error, suberror:byte); {--------------------------------------------------------------------------} implementation {Returns true if PCTCP is installed on the default 61h interrupt vector.} function pctcp_installed:boolean; {Int 61h should have TCPTSR three bytes after the int handler.} var error:byte; begin a:='TCPTSR'; asm push ax push bx push cx push dx push si push di push es push ds pushf mov error,1 mov ax,3561h int 21h add bx,3 mov di,bx {ES:DI points to int vector string} mov dx,seg a mov ds,dx lea si,a {DS:SI points to character array} mov dx,si {save offset of string for future use} mov cx,6 {String is six bytes} cld {Search forward} repe cmpsb {TCPTSR= 84,67,80,84,83,82} sub si,dx {determin offset of first unmatching byte} cmp si,6 jb @error {Value in SI should be 6 or greater} mov error,0 {PCTCP is installed} @error: popf pop ds pop es pop di pop si pop dx pop cx pop bx pop ax end; pctcp_installed := (error = 0); end; {Get the internet address related to a descriptor} procedure get_addr(descriptor:word; var a,b,c,d:byte; var error:word); var regs:registers; begin with regs do begin ah:=$05; bx:=descriptor; end; intr($61,regs); if (regs.flags and fCarry) = fCarry then error := regs.ax else with regs do begin a:=dh; b:=dl; c:=ah; d:=al; error:=0; end; end; {Get network interface statistics.} procedure net_info(descriptor:word; var r:interface_statistics_record; var error:word); var regs:registers; begin regs.ax:=$06; regs.bx:=descriptor; regs.ds:= seg(r); regs.si:= ofs(r); intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error :=regs.ax; end; {Make an allocated network descriptor global} procedure net_globalize(descriptor:word; var global_descriptor,error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$07; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error:=regs.ax else begin global_descriptor := regs.ax; error := 0; end; end; {Release a valid allocated network descriptor} procedure net_release(descriptor:word; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$8; intr($61,regs); if ((regs.flags and fCarry)=fCarry) then error := regs.ax else error :=0; end; {Close all non-global descriptors (globals must be closed individualy)} procedure net_releaseall; var regs:registers; begin regs.ah:=$9; intr($61,regs); end; {Get statistics of TCP connection} procedure net_stat_TCP(var r:TCP_connection_stats; var error:word); var regs:registers; begin regs.ah:=$0c; regs.bx:=$ffff; regs.ds:=seg(r); regs.dx:=ofs(r); intr($61,regs); if((regs.flags and fCarry) = fCarry) then error := regs.ax; end; {Get statistics of IP connection} procedure net_stat_IP(var r:IP_connection_stats; var error:word); var regs:registers; begin regs.ax:=$0c; regs.bx:=$fffe; regs.ds:=seg(r); regs.dx:=ofs(r); intr($61,regs); if((regs.flags and fCarry) = fCarry) then error := regs.ax; end; {Get statistics of UDP connection} procedure net_stat_UDP(var r:UDP_connection_stats; var error:word); var regs:registers; begin regs.ax:=$0c; regs.bx:=$fffd; regs.ds:=seg(r); regs.dx:=ofs(r); intr($61,regs); if((regs.flags and fCarry) = fCarry) then error := regs.ax; end; {Get statistics of ICMP connection} procedure net_stat_ICMP(var r:ICMP_connection_stats; var error:word); var regs:registers; begin regs.ax:=$0c; regs.bx:=$fffc; regs.ds:=seg(r); regs.dx:=ofs(r); intr($61,regs); if((regs.flags and fCarry) = fCarry) then error := regs.ax; end; {Determine if a possible network descriptor is valid.} procedure is_netnd(descriptor:word; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$d; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else regs.ax := 0; end; {disable asynchronous handlers} procedure disable_async; var regs:registers; begin regs.ah:=$11; intr($61,regs); end; {enable asynchronous handlers and return previous state} procedure enable_async(var already_enabled:boolean); var regs:registers; begin regs.ah:=$12; intr($61,regs); already_enabled := (regs.ax<>0); end; {Open a network connection with descriptor. If the descriptor supplied is equal to 0xFFFF a descriptor is automatically allocated and returned in DESCRIPTOR after the connection is made.} procedure net_connect(var descriptor:word; protocol:word; addr_struct:addr; var error:word); var d,p,e:word; var as:addr; regs:registers; begin regs.bx:=descriptor; regs.dx:=protocol; regs.ds:=seg(addr_struct); regs.si:=ofs(addr_struct); regs.ah:=$13; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin error := 0; descriptor := regs.ax; end; end; {Close transmition side of connection. The connection remains open, but data can no longer be transmited onto the network, though data may still be read.} procedure net_eof(descriptor:word; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$18; intr($61,regs); if ((regs.flags and fCarry)=fCarry) then error := regs.ax else error := 0; end; {Imdediatley close a connection} procedure net_abort(descriptor:word; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$19; intr($61,regs); if ((regs.flags and fCarry)=fCarry) then error := regs.ax else error := 0; end; {Write data to the network. seg_buf and off_buf contain the segment and offset of the data to be written. Size contains the length of the data to be written. Upon return written contains the number of bytes acutally written and error contains zero if the write was successful. Written contains zero and error contains the error code if the write was unsuccessful.} procedure net_write(descriptor,size,send_options,seg_buf,off_buf:word; var written,error:word); var regs:registers; begin regs.bx:=descriptor; regs.cx:=size; regs.dx:=send_options; regs.ds:=seg_buf; regs.si:=off_buf; regs.ah:=$1a; intr($61,regs); if ((regs.flags and fCarry)=fCarry) then error := regs.ax else begin written:=regs.ax; error := 0; end; end; {Read data from the network. seg_buff and off_buf contain the segment and offset of the data to be written. Size contains the maximum number of bytes to read. Upon return read contains the number of bytes actually read and error contains zero if the read was successful. Read contains zero and error contains the error code if the read was unsuccessful. seg_addr and off_addr contain the segment and offset of the addr structure for the remote to read from or 0000:0000 for any.} procedure net_read(descriptor,size,receive_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); var regs:registers; begin regs.bx:=descriptor; regs.cx:=size; regs.dx:=receive_options; regs.ds:=seg_buf; regs.si:=off_buf; regs.es:=seg(addr_struct); regs.di:=ofs(addr_struct); regs.ah:=$1b; intr($61,regs); if ((regs.flags and fCarry)=fCarry) then error := regs.ax else begin read:=regs.ax; error := 0; end; end; {Write a datagram to the network. This function differs from net_write in that the addr stucture can be used to override the IP and socket values.} procedure net_writeto(descriptor,size,send_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); var regs:registers; begin regs.bx:=descriptor; regs.cx:=size; regs.dx:=send_options; regs.ds:=seg_buf; regs.si:=off_buf; regs.es:=seg(addr_struct); regs.di:=ofs(addr_struct); regs.ah:=$1c; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin read:=regs.ax; error := 0; end; end; {Read datagram from the network. seg_buff and off_buf contain the segment and offset of the data to be written. Size contains the maximum number of bytes to read. Upon return read contains the number of bytes actually read and error contains zero if the read was successful. Read contains zero and error contains the error code if the read was unsuccessful. seg_addr and off_addr contain the segment and offset of the addr structure for the remote to read from. This function is for use with only datagram or raw descriptors.} procedure net_readfrom(descriptor,size,receive_options,seg_buf,off_buf:word; addr_struct:addr; var read,error:word); var regs:registers; begin regs.bx:=descriptor; regs.cx:=size; regs.dx:=receive_options; regs.ds:=seg_buf; regs.si:=off_buf; regs.es:=seg(addr_struct); regs.di:=ofs(addr_struct); regs.ah:=$1d; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin read:= regs.ax; error:=0; end; end; {Transmit all pending data imediatley} procedure net_flush(descriptor:word; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.ah:=$1e; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else error := 0; end; {Set up asynchronous handler. Upon return seg_old and off_old contain the segment and offset of the old asynchronous handler.} procedure net_asynch(descriptor,event_type,seg_handler,off_handler:word; hint:dword; var seg_old,off_old,error:word); var regs:registers; begin regs.bx:=descriptor; regs.cx:=event_type; regs.ds:=seg_handler; regs.si:=off_handler; regs.es:=hint.a*256 + hint.b; regs.di:=hint.c*256 + hint.d; regs.ah:=$1f; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin seg_old:=regs.ds; off_old:=regs.dx; error := 0; end; end; {allocates a network descriptor. Error = 0 indicates success} procedure net_getdesc(var descriptor, error:word); var regs:registers; begin regs.ah:=$22; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin descriptor := regs.ax; error := 0; end; end; {Listen for incomming connections} procedure net_listen(var descriptor:word; service_type:word; var addr_struct:addr; var error:word); var regs:registers; begin regs.bx:=descriptor; regs.dx:=service_type; regs.ds:=seg(addr_struct); regs.si:=ofs(addr_struct); regs.ah:=$23; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else error := 0; end; {swap two network descriptors} procedure net_swap(var descriptor1,descriptor2,error:word); var regs:registers; begin regs.bx:=descriptor1; regs.cx:=descriptor2; regs.ah:=$24; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin descriptor1:=regs.bx; descriptor2:=regs.cx; error := 0; end; end; {Allocate a global descriptor. Error 0 indicates success.} procedure net_getglobdesc(var descriptor,error:word); var regs:registers; begin regs.ah := $29; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin descriptor := regs.ax; error:=0; end; end; {Reads configuration information from PCTCP driver.} procedure get_configuration_information (var r:configuration_information_record); var regs:registers; begin regs.ax:= seg(r); regs.si:= ofs(r); regs.ah:=$2a; intr($61,regs); end; {Returns the host IP address in network byte order given the hostname. The hostname is resolved by numerical, host table, DNS or IEN116 methods respectively.} procedure nm_res_name(var a,b,c,d:byte;{Host IP address in Network Byte Order} length:word; {length of host name} host:hostname;{ASCIZ character array hostname} var error:word); var regs:registers; begin regs.cx:=length; regs.ds:=seg(host); regs.dx:=ofs(host); regs.es:=0; regs.di:=0; regs.ah:=$54; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then error := regs.ax else begin a:= regs.dh; b:= regs.dl; c:= regs.ah; d:= regs.al; error := 0; end; end; {Sends a specified amount of random data to the specified host which should be echoed back. 0 in error indicates a success.} procedure icmp_ping(a,b,c,d:byte; {Host IP address in Network Byte Order} len:byte; {Length of data to send} var error, suberror:byte); var regs:registers; begin regs.ah:=$30; regs.bh:=b; regs.bl:=a; regs.dh:=d; regs.dl:=c; regs.cx:=len; intr($61,regs); if ((regs.flags and fCarry) = fCarry) then begin error := regs.al; suberror := regs.ah; end else error := 0; end; end.