Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10 5/3/83; site k.cs.cmu.edu Path: utzoo!watmath!clyde!burl!ulysses!mhuxr!mhuxn!ihnp4!qantel!lll-crg!seismo!rochester!pt.cs.cmu.edu!k.cs.cmu.edu!tim From: tim@k.cs.cmu.edu (Tim Maroney) Newsgroups: net.sources.mac Subject: Macintosh Internet Protocols (7 of 12) (TCP) Message-ID: <667@k.cs.cmu.edu> Date: Tue, 26-Nov-85 05:37:32 EST Article-I.D.: k.667 Posted: Tue Nov 26 05:37:32 1985 Date-Received: Fri, 29-Nov-85 21:21:24 EST Organization: Carnegie-Mellon University, Networking Lines: 1738 echo extracting net/tcp_lib.text... cat >net/tcp_lib.text <<'!E!O!F!' {$X-} {$M+} {$R-} {$0V-} {$D-} {$DECL DEBUG} {$SETC DEBUG := false} {$DECL SAVEREST} {$SETC SAVEREST := false} {$DECL LISTEN} {$SETC LISTEN := true} { Please note the copyright notice in the file "copyright/notice" } UNIT TCP_Lib; INTERFACE {$L-} USES {$U-} {$U Obj-Memtypes } Memtypes, {$U Obj-QuickDraw } QuickDraw, {$U Obj-OSIntf } OSIntf, {$U Obj-ToolIntf } ToolIntf, {$U Obj-PackIntf } PackIntf, {$U Obj-ABPasIntf } ABPasIntf, {$U net-Task_Lib } Task_Lib, {$U net-Timer_Lib } Timer_Lib, {$U net-err_lib } Err_Lib, {$U net-ip_lib } IP_Lib, {$U net-term_lib } Term_Lib, {$U net-calls } Call_Lib; {$L+} CONST TCPWINDOW = 1000; { normal advertised window } TCPLOWIND = 500; { low water mark on window } TCPRTK = 3072; TCPSTK = 3072; TCPTKSZ = TCPRTK + TCPSTK; BUFSIZE = 2048; TOTALCHUNKS = 16; TYPE unsigned = Integer; seq_t = LongInt; unshort = Integer; seq_no = RECORD CASE Integer OF 0: (a: seq_t); 1: (l: Integer; h: Integer); END; { p } Ref_tcp = ^tcp; tcp = PACKED RECORD { a tcp header } tc_srcp: unshort; { source port } tc_dstp: unshort; { dest port } tc_seq: seq_t; { sequence number } tc_ack: seq_t; { acknowledgement number } tc_thl: 0..15; tc_res1: 0..15; tc_res2: 0..3; tc_furg: BOOLEAN; tc_fack: BOOLEAN; tc_psh: BOOLEAN; tc_rst: BOOLEAN; tc_syn: BOOLEAN; tc_fin: BOOLEAN; tc_win: unshort; { window } tc_cksum: unshort; { checksum } tc_urg: unshort; { urgent pointer } END; { TCP pseudo-header structure, used for checksumming } tcpph = PACKED RECORD { psuedo-header } tp_src: in_name; { source addr } tp_dst: in_name; { dest addr } tp_zero: BYTE; { always 0 } tp_pro: BYTE; { protocol } tp_len: Integer; { length } END; Ref_tcpph = ^ tcpph; Ref_tcconn = ^tcp_conn; tcp_conn = PACKED record { record representing a TCP connection } tc_next: Ref_tcconn; { queue link } tc_type: QTypes; { type word for Mac queues } conn_state: Integer; { connection state } ForeignHost: in_name; { what foreign host it's connected to } SourcePort: Integer; { id of local port } DestPort: Integer; { id of remote port } TelLowWin: Integer; TelWin: Integer; tcptm: Ref_Timer; { The resend timer } tmack: Ref_Timer; { ack timer } opbi: PACKET; { ptr to output packet buffer } otp: Ref_tcp; { ptr to output pkt tcp hdr } odp: PTR; { ptr to start of output pkt data } ophp: Ref_tcpph; { tcp pseudo hdr for cksum calc } frn_win: unsigned; { Size of foreign window. } ack_time: LongInt; { When last ACK was sent. } odlen: Integer; { bytes of data in output pkt } dally_time: Integer; { time to delay ack (ticks) } retry_time: Integer; { retransmission timeout (ticks)} resend: Integer; { flag indicating data must be resent } taken: Integer; { number of bytes of circbuf processed } avail: Integer; { number of circbuf bytes unprocessed } fin_offset: Integer; { offset to finish of data } circbuf: packed array [0..BUFSIZE-1] of char; { data buffer } numchunks: Integer; maxchunks: Integer; lastchunk: Integer; ceilchunk: Integer; first: packed array [0..TOTALCHUNKS-1] of Integer; last: packed array [0..TOTALCHUNKS-1] of Integer; free: packed array [0..TOTALCHUNKS-1] of Boolean; {int (*tc_ofcn)();} { user function called on open } {int (*tc_dispose)();} { user function to receive data } {int (*tc_yield)();} { Predicate set when user must run. } {int (*tc_cfcn)();} { user function called on close } {int (*tc_tfcn)();} { user function called on icp tmo } {int (*tc_rfcn)();} { user function called on icp resend } {int (*tc_buff)();} { user function to upcall when output buffer space is available } tc_ofcn, tc_dispose, tc_yield, tc_cfcn, tc_tfcn, tc_rfcn, tc_buff: ProcPtr; blk_inpt: Boolean; { prevents new data after close req } fin_rcvd: Boolean; { received end of file (FIN) } newsend: Boolean; { flag indicating new data to send } hasrecvd: Boolean; { does connection have data to process ? } mustsend: Boolean; { does connection need a send done? } end; PROCEDURE tcp_init(stksiz: Integer); FUNCTION tcp_open(fh: Ref_in_name; fs: unsigned; ls: unsigned; win:Integer; lowwin: Integer; ofcn,infcn,yldfcn,cfcn,tmofcn,rsdfcn,buff:ProcPtr) :Ref_tcconn; {$IFC LISTEN} FUNCTION tcp_listen(ls: unsigned; win:Integer; lowwin: Integer; ofcn,infcn,yldfcn,cfcn,tmofcn,rsdfcn,buff:ProcPtr) :Ref_tcconn; {$ENDC} PROCEDURE tcp_close(connection:Ref_tcconn); FUNCTION tc_fput(connection:Ref_tcconn; c: char): Boolean; FUNCTION tc_put(connection:Ref_tcconn; c: char): Boolean; PROCEDURE tcpurgent(connection:Ref_tcconn); PROCEDURE tcp_ex(connection:Ref_tcconn); PROCEDURE tc_status(connection:Ref_tcconn); { tcp connection states } CONST CLOSED = 0; { nothing has happenned } { CLOSED,SYNSENT,ESTAB,TIMEWAIT same as in tcp spec } SYNSENT = 1; { connection requested } SYNRCVD = 2; ESTAB = 3; { connection established } FINSENT = 4; { local user wishes to close } { same as FIN-WAIT-1 in tcp spec } FINRCVD = 5; { foreign host wishes to close } { same as CLOSE-WAIT in tcp spec } SIMUL = 6; { local user and foreign host wish to close } { same as CLOSING in tcp spec } FINACKED = 7; { local user's close request acked } { same as FIN-WAIT-2 in tcp spec } R_AND_S = 8; { frgn close req rcvd, local close req sent } { same as LAST-ACK in tcp spec } TIMEWAIT = 9; { last ack is being sent } LISTEN = 10; { waiting for connection request } { new for Macintosh version } { Picture from TCP Specs (RFC 793): TCP Header Format 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ } IMPLEMENTATION {$IFC DEBUG} PROCEDURE WriteHex(c:char); { writes a char as two hex digits } VAR temp:Integer; BEGIN temp := BitAnd(BitSR(ord(c),4),$000f); if (temp <= 9) then temp := temp + ord('0') else temp := temp + ord('A') - 10; Write(chr(temp)); temp := BitAnd(ord(c),$000f); if (temp <= 9) then temp := temp + ord('0') else temp := temp + ord('A') - 10; Write(chr(temp)); END; PROCEDURE Print_TCP(t:Ref_TCP); BEGIN Write('TCP Header: '); WriteLn('Source Port = ',t^.tc_srcp:1,', Dest Port = ',t^.tc_dstp:1); WriteLn('Sequence number = ',t^.tc_seq:1,', Ack number = ',t^.tc_ack:1); { Write('Bits = '); WriteHex(chr(BitAND($00ff,BitSR(t^.tc_bits,8)))); WriteHex(chr(BitAND($00ff,t^.tc_bits))); } Write('Window = ',t^.tc_win:1); WriteLn(', Checksum = ',t^.tc_cksum:1,', Urgent Pointer = ',t^.tc_urg:1); END; {$ENDC} CONST TCPPROT = 6; { tcp protocol number } TCPMAXWIND = 4096; { maximum tcp window that this implementation supports } { Translation of Dave Clark's Alto TCP into C. This file contains a bare-bones minimal TCP, suitable only for user Telnet and other protocols which do not need to transmit much data. Its primary weakness is that it does not pay any attention to the window advertised by the receiver; it assumes that it will never be sending much data and hence will never run out of receive window. (Note: this not really that much of a weakness, since the other side should ignore any data over its window. Still, it would be more efficient.) Its other major weakness is that it will not handle out-of-sequence packets; any out-of-sequence data received is ignored. This TCP requires the tasking package to run. It runs as two tasks: TCPsend, which handles all data transmission, and TCPprocess, which processes the incoming data from the circular buffer. The data is placed in the circular buffer by tcp_rcv, which is upcalled from internet on an arriving packet. } CONST ACKDALLY = 30; { wait between ACKs (ticks) } INITRT = 4; { retry time -- initial request } {$IFC DEBUG} CONNRT = 20; {$ELSEC} CONNRT = 2; { retry time -- after connected } {$ENDC} MAXBUF = 500; { maximum output buffer size } ICPTMO = 5; { initial conn. timeout } { Connection queue structure: compatible with Mac queues } TYPE TCconnQ = PACKED RECORD qFlags:INTEGER; qHead:Ref_tcconn; qTail:Ref_tcconn end; TCPGlobals = RECORD tcpconns:^TCconnQ; { Global connection queue } still_data: Boolean; { There are still incoming pkts to process. } tcpfd: IPCONN; { connection ID used when calling Internet } TCPsend: Ref_Task; { tcp sending task } TCPprocess: Ref_Task; { TCP data processing task } first_offset: Integer; { TCP statistics collection variables } tcppsnt:unsigned; { number of packets sent } tcpprcv:unsigned; { number of packets received } tcpbsnt:unsigned; { number of bytes sent } tcpbrcv:unsigned; { number of bytes received } tcprack:unsigned; { # of bytes received and acked } tcpsock:unsigned; { # of packets not for my socket } tcpresend:unsigned; { # of resent packets } tcprercv:unsigned; { # of old packets received } tcpous:unsigned; { # of out-of-sequence packets } bd_chk:unsigned; { Number of pkts rcvd with bad checksums.} ign_win:unsigned; { Num pkts rcvd that ignored our window. } END; Ref_TCPGlobals = ^TCPglobals; VAR global:Ref_TCPGlobals; PROCEDURE tcp_rcv(inpkt: PACKET; len: Integer; fhost: in_name); FORWARD; PROCEDURE tcp_send(NoArg:PTR); FORWARD; PROCEDURE tcp_process(NoArg:PTR); FORWARD; PROCEDURE tmhnd(connection:Ref_tcconn); FORWARD; PROCEDURE tcp_ack(connection:Ref_tcconn); FORWARD; PROCEDURE send_wake(connection:Ref_tcconn); FORWARD; { translated macro } FUNCTION NOT_YET(a, b: Integer): Boolean; BEGIN NOT_YET := ((a) < (b)); END; { NOT YET } { This routine is called to initialize the TCP. It starts up the tasking system, initiates the timer, TCPsend, and TCPrcv tasks. It does not attempt to open the connection; that function is performed by tcp_open. } {$S InitSeg} PROCEDURE tcp_init(stksiz: Integer); BEGIN global := POINTER(ORD4(NewPtr(sizeof(TCPglobals)))); if global = NIL then Fatal(StrCvt('TCP_INIT: can''t get globals'),false); WITH global^ DO BEGIN tcpfd := in_open(TCPPROT, @tcp_rcv); if (tcpfd = NIL) THEN Fatal(StrCvt('TCP_INIT: can''t open Internet'),false); TCPconns := POINTER(ORD4(NewPtr(sizeof(TCconnQ)))); if TCPconns = NIL then Fatal(StrCvt('TCP_INIT: can''t allocate connection queue'),false); { Initialize connection queue } TCPconns^.qFlags := 0; TCPconns^.qHead := NIL; TCPconns^.qTail := NIL; tcppsnt := 0; { initially, no pkts sent } tcpprcv := 0; { or received } tcpbsnt := 0; { no bytes sent } tcpbrcv := 0; { foreign host sent no bytes } tcprack := 0; { we acked no bytes of foreign host} tcpresend := 0; tcprercv := 0; ign_win := 0; still_data := FALSE; TCPsend := tk_fork(tk_cur, @tcp_send, TCPSTK, 'TCPsend', NIL); TCPprocess := tk_fork(TCPsend, @tcp_process, TCPRTK, 'TCPproc', NIL); { let the other tasks get started } tk_yield; END; { WITH } END; { end of tcp_init} {$S } { tcp_alloc: allocate and return a tcp connection (for now just one allowed) the pointers to the up-callable user routines for open, received data, close and timeout. } FUNCTION tcp_alloc:Ref_tcconn; VAR Dummy:Boolean; connection:Ref_tcconn; i:Integer; Fake:OSErr; BEGIN { Get connection from memory manager; put on queue of connections in use } connection := POINTER(ORD4(NewPtr(sizeof(tcp_conn)))); if connection = NIL then begin CantAlloc(StrCvt('TCP'),StrCvt('connection')); tcp_alloc := NIL; exit(tcp_alloc); end; connection^.tc_next := NIL; connection^.tc_type := dummyType; enqueue(POINTER(ORD4(connection)),POINTER(ORD4(global^.TCPconns))); WITH connection^ do begin { alloc and set up output pkt } opbi := in_alloc(INETLEN, 0); if (opbi = NIL) THEN BEGIN CantAlloc(StrCvt('TCP'),StrCvt('packet')); Fake := dequeue(POINTER(ORD4(connection)), POINTER(ORD4(global^.TCPconns))); DisposPtr(POINTER(ORD4(connection))); tcp_alloc := NIL; exit(tcp_alloc); END; otp := POINTER(ORD4(in_data(in_head(opbi)))); odp := POINTER(ORD4(otp) + sizeof(tcp)); { allocate pseudoheader for outgoing packet } ophp := POINTER(ORD4(NewPtr(sizeof(tcpph)))); if (ophp = NIL) THEN BEGIN Fake := dequeue(POINTER(ORD4(connection)), POINTER(ORD4(global^.TCPconns))); DisposPtr(POINTER(ORD4(connection))); tcp_alloc := NIL; exit(tcp_alloc); END; ophp^.tp_zero := 0; ophp^.tp_pro := TCPPROT; global^.tcpsock := 0; conn_state := CLOSED; { initially, conn. closed } retry_time := INITRT*TPS; newsend := FALSE; ForeignHost := 0; resend := 0; fin_offset := 0; fin_rcvd := FALSE; ack_time := 0; odlen := 0; blk_inpt := FALSE; hasrecvd := FALSE; mustsend := FALSE; otp^.tc_seq := 0; otp^.tc_ack := 0; otp^.tc_res1 := 0; otp^.tc_res2 := 0; otp^.tc_thl := 0; otp^.tc_furg := false; otp^.tc_fack := false; otp^.tc_psh := false; otp^.tc_rst := false; otp^.tc_fin := false; otp^.tc_syn := true; otp^.tc_urg := 0; global^.bd_chk := 0; taken := -1; avail := -1; tcptm := tm_alloc; if(tcptm = NIL) THEN BEGIN Fake := dequeue(POINTER(ORD4(connection)), POINTER(ORD4(global^.TCPconns))); DisposPtr(POINTER(ORD4(connection))); tcp_alloc := NIL; exit(tcp_alloc); END; tmack := tm_alloc; if(tmack = NIL) THEN BEGIN Dummy := tm_free(tcptm); Fake := dequeue(POINTER(ORD4(connection)), POINTER(ORD4(global^.TCPconns))); DisposPtr(POINTER(ORD4(connection))); tcp_alloc := NIL; exit(tcp_alloc); END; numchunks := 0; maxchunks := 0; lastchunk := 0; ceilchunk := 0; FOR i := 0 to TOTALCHUNKS - 1 DO free[i] := TRUE; end;{with} tcp_alloc := connection; END; PROCEDURE tcp_free(VAR conn:Ref_tcconn); VAR Dummy:OSErr; Fake:Boolean; BEGIN { dequeue the connection from the active list and the tcp_process queue, then free its storage } Dummy := dequeue(POINTER(ORD4(conn)),POINTER(ORD4(global^.TCPconns))); in_free(conn^.opbi); Fake := tm_clear(conn^.tcptm); Fake := tm_free(conn^.tcptm); Fake := tm_clear(conn^.tmack); Fake := tm_free(conn^.tmack); DisposPtr(POINTER(ORD4(conn^.ophp))); DisposPtr(POINTER(ORD4(conn))); conn := NIL END; { Initiate the TCP closing sequence. This routine will return immediately; when the close is complete the user close function will be called. } PROCEDURE tcp_close(connection:Ref_tcconn); BEGIN if connection = NIL then exit(tcp_close); WITH connection^ do begin CASE conn_state OF LISTEN, SYNRCVD, CLOSED: BEGIN CALL(tc_cfcn); tcp_free(connection); END; ESTAB: BEGIN conn_state := FINSENT; otp^.tc_fin := true; otp^.tc_psh := false; blk_inpt := TRUE; newsend := TRUE; send_wake(connection); END; SYNSENT: BEGIN otp^.tc_rst := TRUE; blk_inpt := TRUE; newsend := TRUE; send_wake(connection); END; END; end;{with} END; { end of tcp_close } { Close and reset a tcp connection. } PROCEDURE tc_clrs(connection:Ref_tcconn; p: PACKET; fhost: in_name); VAR ltemp: LongInt; myport:unsigned; ptp: Ref_tcp; php: tcpph; err:Integer; BEGIN if connection = NIL then exit(tc_clrs); WITH connection^,global^ do begin ptp := POINTER(ORD4(in_data(in_head(p)))); { swap port numbers } myport := ptp^.tc_dstp; ptp^.tc_dstp := ptp^.tc_srcp; ptp^.tc_srcp := myport; { fill in the rest of the header } ltemp := ptp^.tc_seq; ptp^.tc_seq := ptp^.tc_ack; ptp^.tc_ack := ltemp; ptp^.tc_furg := false; ptp^.tc_fack := false; ptp^.tc_psh := false; ptp^.tc_fin := false; ptp^.tc_syn := false; ptp^.tc_thl := sizeof(tcp) div 4; ptp^.tc_rst := true; ptp^.tc_win := 0; {tcp_swab(ptp);} { Not on 68000 } { Set up the tcp pseudo header } php.tp_src := in_mymach(fhost); php.tp_dst := fhost; php.tp_zero := 0; php.tp_pro := TCPPROT; php.tp_len := sizeof(tcp); { checksum the packet } ptp^.tc_cksum := cksum(@php, sizeof(tcpph) div 2); ptp^.tc_cksum := BitNOT(cksum(POINTER(ORD4(ptp)),sizeof(tcp) div 2)); err := in_write(tcpfd, p, sizeof(tcp), fhost); end;{with} tcp_free(connection); END; {tc_clrs} PROCEDURE cleanup(why: StringPtr); BEGIN Error2(StrCvt('Closed:'),why); END; { Just shift the data in a buffer, moving len bytes from from to to. } PROCEDURE shift(from_ptr, to_ptr: PTR; len:Integer); BEGIN {$IFC DEBUG} if (len < 0) THEN BEGIN WriteLn('tcp: shift: bad arg--len < 0'); EXIT(shift); END; {$ENDC} WHILE (len<>0) DO BEGIN len := len - 1; to_ptr^ := from_ptr^; to_ptr := POINTER(ORD4(to_ptr)+1); from_ptr := POINTER(ORD4(from_ptr)+1); END; END; { shift } { This routine forms the body of the TCP data receiver task. It attempts to read and process incoming packets. The processing of each packet is divided into three phases: 1) Processing acknowlegments. This involves 'shifting' the data in the output packet to account for acknowledged data. 2) Processing state information: syn's, fin's, urgents, etc. 3) Processing the received data. This is done by calling the user's 'input data' function (specified in his call to tcp_open), passing it the address and length of the received data. } PROCEDURE tcp_rcv(inpkt: PACKET; len: Integer; fhost: in_name); VAR itp: Ref_tcp; { input pkt tcp hdr } idp: PTR; { input pkt data ptr } needed_acking: Integer; { number of outstanding bytes } acked: Integer; { # outstanding bytes acked by this packet } data_acked: Integer; { # outstanding bytes acked by this packet } { number of previously received seq. numbers} prev_rcvd: Integer; { (data + fin bit) in current packet} idlen: Integer; { len of input pkt in bytes } i: Integer; limit: Integer; Raw_Ptr: PTR; tempsum: unsigned; { temp variable for a checksum } Dummy: Boolean; connection: Ref_tcconn; ip_hd: Ref_IP; { used to extract fhost for checksum } start_offset: Integer; end_offset: Integer; notfound: Boolean; iphp: tcpph; { pseudo-header for checksum calcs } Fake: OSErr; BEGIN CheckTask; WITH global^ DO BEGIN { Process the received input packet } itp := POINTER(ORD4(in_data(in_head(inpkt)))); idp := POINTER(ORD4(itp) + (itp^.tc_thl*4)); { get incoming data length } idlen := len - (itp^.tc_thl * 4); tcpprcv := tcpprcv + 1; { another packet received } {$IFC DEBUG} WriteLn('TCP: Received packet, len = ',len:1,', idlen = ',idlen:1); (* Print_TCP(itp); *) {$ENDC} { compute incoming tcp checksum here... } if idlen >= 0 THEN BEGIN Raw_Ptr := POINTER(ORD4(idp) + idlen); Raw_Ptr^ := 0; END; iphp.tp_zero := 0; iphp.tp_pro := TCPPROT; iphp.tp_len := idlen + (itp^.tc_thl*4); ip_hd := in_head(inpkt); iphp.tp_dst := in_mymach(ip_hd^.ip_src); iphp.tp_src := ip_hd^.ip_src; tempsum := itp^.tc_cksum; itp^.tc_cksum := cksum(@iphp,sizeof(tcpph) div 2); itp^.tc_cksum := BitNOT(cksum(POINTER(ORD4(itp)), ((idlen+1) div 2)+(itp^.tc_thl*2))); if (itp^.tc_cksum <> tempsum) THEN BEGIN {$IFC DEBUG} WriteLn('TCP: Bad xsum was ',tempsum:1, '; should have been ',itp^.tc_cksum:1); {$ENDC} bd_chk := bd_chk + 1; in_free(inpkt); EXIT(tcp_rcv); END; { loop over each connection looking for match (demux) } connection := TCPconns^.qHead; notfound := true; while (connection <> NIL) and notfound do begin { If the user wishes to send data, give up the processor. } if (CALLB(connection^.tc_yield)) THEN BEGIN still_data := TRUE; { There's still data to process. } tk_yield; still_data := FALSE; END; if (itp^.tc_dstp = connection^.SourcePort) then begin if (connection^.conn_state = CLOSED) | ((connection^.conn_state <> LISTEN) & (itp^.tc_srcp <> connection^.DestPort)) THEN BEGIN tcpsock := tcpsock + 1; in_free(inpkt); EXIT(tcp_rcv); END; notfound := false; end else connection := connection^.tc_next; end; { of loop } if notfound then BEGIN {$IFC DEBUG} WriteLn('Received TCP packet not for me'); {$ENDC} tcpsock := tcpsock + 1; in_free(inpkt); exit(tcp_rcv); END; WITH connection^ DO BEGIN if itp^.tc_rst THEN BEGIN IF (conn_state <> LISTEN) AND (conn_state <> CLOSED) THEN { other guy's resetting me } BEGIN cleanup(StrCvt('foreign reset')); CALL(tc_cfcn); tcp_free(connection); END; in_free(inpkt); EXIT(tcp_rcv); END; { incoming packet processing is dependent on the state of the connection } if (conn_state = SYNSENT) THEN BEGIN { opening the connection } { ack must be for our initial sequence number - namely, 1 } if (NOT itp^.tc_fack) OR (itp^.tc_ack <> 1) THEN BEGIN in_free(inpkt); EXIT(tcp_rcv); END; if NOT itp^.tc_syn THEN BEGIN in_free(inpkt); EXIT(tcp_rcv); END; { Connection open } otp^.tc_syn := false; otp^.tc_rst := false; otp^.tc_fack := true; otp^.tc_psh := true; otp^.tc_seq := 1; itp^.tc_seq := itp^.tc_seq + 1; { syn's take sequence no.space } otp^.tc_ack := itp^.tc_seq; tcpbrcv := 1; frn_win := itp^.tc_win; conn_state := ESTAB; retry_time := CONNRT*TPS; { Our initial request was acknowledged and there is not yet new data so reset the timer } newsend := TRUE; Dummy := tm_clear(tcptm); CALL(tc_ofcn); send_wake(connection); END {$IFC LISTEN} else if conn_state = LISTEN then BEGIN { New code (for Mac version) to accept incoming connections } if (NOT itp^.tc_syn) OR itp^.tc_fack THEN BEGIN tc_clrs(connection,inpkt,fhost); in_free(inpkt); EXIT(tcp_rcv); END; conn_state := SYNRCVD; ForeignHost := fhost; DestPort := itp^.tc_srcp; { set up pseudoheader for outgoing packet } ophp^.tp_src := in_mymach(ForeignHost); ophp^.tp_dst := ForeignHost; { set up outgoing packet } otp^.tc_srcp := SourcePort; otp^.tc_dstp := DestPort; otp^.tc_ack := itp^.tc_seq + 1; otp^.tc_fack := true; newsend := TRUE; send_wake(connection); in_free(inpkt); EXIT(tcp_rcv); END else if (conn_state = SYNRCVD) then BEGIN { New (Mac) code to accept acknowledgement of opening } if itp^.tc_seq <> otp^.tc_ack then begin {$IFC DEBUG} WriteLn('TCP_RCV: bad seq number (',itp^.tc_seq:1, ') should be (',otp^.tc_seq:1,')'); {$ENDC} tc_clrs(connection,inpkt,fhost); in_free(inpkt); EXIT(tcp_rcv); end; if NOT otp^.tc_fack then begin in_free(inpkt); EXIT(tcp_rcv); end; if itp^.tc_ack <> (otp^.tc_seq + 1) then begin {$IFC DEBUG} WriteLn('TCP_RCV: bad ack number (',itp^.tc_ack:1, '); should be ',otp^.tc_ack:1); {$ENDC} tc_clrs(connection,inpkt,fhost); in_free(inpkt); EXIT(tcp_rcv); end; { Connection open } otp^.tc_syn := false; otp^.tc_rst := false; otp^.tc_fack := true; otp^.tc_psh := true; otp^.tc_seq := itp^.tc_seq; otp^.tc_ack := itp^.tc_ack; tcpbrcv := 1; frn_win := itp^.tc_win; conn_state := ESTAB; retry_time := CONNRT*TPS; Dummy := tm_clear(tcptm); CALL(tc_ofcn); END {$ENDC} ; { This code updates things based on incoming ack value } if itp^.tc_fack THEN BEGIN acked := ORD(itp^.tc_ack - otp^.tc_seq); needed_acking := odlen + ORD(otp^.tc_fin); IF acked = needed_acking THEN data_acked := acked - ORD(otp^.tc_fin) ELSE data_acked := acked; {$IFC DEBUG} WriteLn('tcp_rcv: ACK processing (acked = ',acked:1, ', needed_acking = ',needed_acking:1,')'); {$ENDC} { Ack for unsent data prompts us to reset. } if acked > needed_acking THEN BEGIN otp^.tc_rst := true; Error( StrCvt('TCP_RCV: unsent data acknowledged (resetting)')); otp^.tc_seq := itp^.tc_ack; send_wake(connection); in_free(inpkt); EXIT(tcp_rcv); END; if acked > 0 THEN BEGIN { So now we have some free output buffer space. Upcall the client and tell him so. } if (odlen >= MAXBUF) THEN CALL(tc_buff); { Account for ack of our urgent data by updating the urgent pointer } if otp^.tc_furg THEN BEGIN otp^.tc_urg := otp^.tc_urg - acked; if (otp^.tc_urg <= 0) THEN BEGIN otp^.tc_urg := 0; otp^.tc_furg := false; END; END; { See if all our outgoing data is now acked. If so, turn off resend timer } if acked = needed_acking THEN BEGIN Dummy := tm_clear(tcptm); newsend := FALSE; resend := 0; odlen := 0; odp^ := 0; if otp^.tc_fin THEN BEGIN otp^.tc_fin := false; CASE (conn_state) OF FINSENT: conn_state := FINACKED; SIMUL: conn_state := TIMEWAIT; R_AND_S: BEGIN CALL(tc_cfcn); tcp_free(connection); in_free(inpkt); EXIT(tcp_rcv); END; {$IFC DEBUG} OTHERWISE BEGIN WriteLn('tcp_rcv: unexpected state: ',conn_state:1); END; {$ENDC} END; { end of case } END; { end of fin test } END { if acked = needed_acking } ELSE { acked is less than needed_acking } BEGIN { Otherwise, shift the output data in the packet to account for ack } odlen := odlen - data_acked; shift(POINTER(ORD4(odp)+data_acked), odp, odlen); Raw_Ptr := POINTER(ORD4(odp) + odlen); Raw_Ptr^ := 0; END; { update the sequence number } frn_win := itp^.tc_win; otp^.tc_seq := otp^.tc_seq + acked; END; END; { Now process the received data } {$IFC DEBUG} WriteLn('tcp_rcv: About to process data'); {$ENDC} { Check if the incoming data is over the top of our window } if ((itp^.tc_seq + idlen) > (otp^.tc_ack + otp^.tc_win)) THEN BEGIN { ignore excess data } ign_win := ign_win + 1; idlen := idlen - (itp^.tc_seq + idlen - (otp^.tc_ack + otp^.tc_win)); END; { Calculate the number of previously received sequence numbers in the current packet } prev_rcvd := otp^.tc_ack - itp^.tc_seq; start_offset := 0 - prev_rcvd; end_offset := (start_offset + idlen) - 1; if end_offset >= BUFSIZE THEN end_offset := BUFSIZE - 1; {$IFC DEBUG} WriteLn('tcp_rcv: prev_rcvd = ',prev_rcvd:1,', end_offset = ',end_offset:1, ', idlen = ',idlen:1,', avail = ',avail:1); {$ENDC} if itp^.tc_fin THEN BEGIN fin_rcvd := TRUE; fin_offset := end_offset; END; if end_offset < avail THEN { If all incoming info is old.} BEGIN {$IFC DEBUG} WriteLn('tcp_rcv: no new data in packet'); {$ENDC} tcprercv := tcprercv + 1; in_free(inpkt); EXIT(tcp_rcv); END; limit := prev_rcvd; if limit < 0 then limit := 0; FOR i := limit TO idlen - 1 DO BEGIN Raw_Ptr := POINTER(ORD4(idp)+i); circbuf[(taken+1+start_offset+i) mod BUFSIZE] := chr(Raw_Ptr^); END; if (numchunks = 0) THEN BEGIN if start_offset <= avail + 1 THEN { DDC CHANGE } avail := end_offset else BEGIN first[0] := start_offset; last[0] := end_offset; free[0] := FALSE; numchunks := 1; maxchunks := 0; lastchunk := 0; END; END else BEGIN if (lastchunk > -1) & ((last[lastchunk] + 1) = start_offset) THEN last[lastchunk] := end_offset else BEGIN FOR i := 0 TO maxchunks DO BEGIN if free[i] THEN cycle; if (last[i]+1) < start_offset THEN cycle; if first[i] > (end_offset+1) THEN cycle; if end_offset < last[i] then end_offset := last[i]; if start_offset > first[i] then start_offset := first[i]; free[i] := TRUE; numchunks := numchunks - 1; if (numchunks = 0) THEN BEGIN maxchunks := -1; leave; END; END; if (lastchunk > -1) & free[lastchunk] THEN lastchunk := -1; if (start_offset <= 0) THEN avail := end_offset else BEGIN limit := maxchunks + 1; if limit > TOTALCHUNKS-1 then limit := TOTALCHUNKS - 1; FOR i := 0 TO limit DO BEGIN if not free[i] THEN cycle; free [i] := FALSE; first[i] := start_offset; last [i] := end_offset; numchunks := numchunks + 1 ; if i = (maxchunks + 1) THEN maxchunks := i; if i > ceilchunk THEN ceilchunk := i; if lastchunk = -1 THEN lastchunk := i; leave; END; END; END; END; {$IFC DEBUG} WriteLn('tcp_rcv: avail = ',avail:1,', fin_offset = ',fin_offset:1); {$ENDC} { If there's data to process, wake up the data processing task. } if (avail >= 0) OR (fin_offset = -1) THEN BEGIN {$IFC DEBUG} WriteLn('tcp_rcv: awakening tcp_process'); {$ENDC} hasrecvd := true; tk_wake(TCPprocess); END; in_free(inpkt); end; { WITH connection^ } end; { WITH global^ } END; {tcp_rcv} { Process received data from tcp. This is the task counterpart of tcp_rcv. } PROCEDURE tcp_process(NoArg:PTR); LABEL 666; VAR i: Integer; { used for looping over data } Fake: Boolean; connection: Ref_tcconn; ticks: LONGINT; { used to regulate SystemTask calling } BEGIN tk_block; { Forget the initial wakeup } 666: while true do begin connection := global^.tcpconns^.qHead; while connection <> NIL do if connection^.hasrecvd then leave else connection := connection^.tc_next; if connection = NIL then tk_block else leave; END; {$IFC DEBUG} WriteLn('tcp_process: running....'); {$ENDC} WITH connection^,global^ do begin {$IFC DEBUG} WriteLn('tcp_process: avail: ',avail:1,', fin_offset: ',fin_offset:1); {$ENDC} if (avail >= 0) OR (fin_offset = -1) THEN BEGIN while in_more do begin {$IFC DEBUG} WriteLn('tcp_process: yielding due to in_more'); {$ENDC} tk_yield; end; hasrecvd := false; {$IFC DEBUG} WriteLn('tcp_process: about to dispose, avail = ',avail:1); {$ENDC} ticks := tickcount; FOR i := 0 TO avail DO BEGIN CALL1C(circbuf[(taken+1+i) mod BUFSIZE],tc_dispose); if tickcount > ticks then begin SystemTask; ticks := tickcount; end; END; FOR i := maxchunks DOWNTO 0 DO BEGIN if free[i] THEN BEGIN if i = maxchunks then maxchunks := maxchunks - 1; cycle; END; first[i] := first[i] - (avail + 1); last[i] := last[i] - (avail + 1); END; if fin_rcvd THEN fin_offset := (fin_offset - avail) - 1; taken := (taken + avail + 1) mod BUFSIZE; if fin_rcvd THEN BEGIN if fin_offset = -1 THEN BEGIN { foreign close request prompts connection state change } CASE (conn_state) OF ESTAB: BEGIN conn_state := FINRCVD; otp^.tc_fin := true; conn_state := R_AND_S; END; FINSENT: conn_state := SIMUL; FINACKED: conn_state := TIMEWAIT; {$IFC DEBUG} OTHERWISE BEGIN WriteLn('TCP_RCV: bad state: ', conn_state:1); END; {$ENDC} END; { end of CASE } { ack foreign close request } { fin's take up seq. no. space } otp^.tc_ack := otp^.tc_ack + 1; tcpbrcv := tcpbrcv + 1; send_wake(connection); END; END; otp^.tc_ack := otp^.tc_ack + avail + 1; tcpbrcv := tcpbrcv + avail + 1; otp^.tc_win := otp^.tc_win - (avail + 1); if (otp^.tc_win < TelLowWin) | (NOT in_more) THEN send_wake(connection); avail := -1; { make sure to avoid silly window syndrome } { this used to be done in tcp_send, but if there was nothing to send, TCP would stop accepting data until there was something to send! } if { (NOT still_data) & } (otp^.tc_win < TelLowWin) THEN otp^.tc_win := TelWin; Fake := tm_clear(tmack); dally_time := ack_time - TickCount; if dally_time > 0 THEN tm_tset(dally_time, @tcp_ack, POINTER(ORD4(connection)), tmack) ELSE send_wake(connection); END; end; { WITH } goto 666; END; { tcp_process } PROCEDURE send_wake(connection:Ref_tcconn); BEGIN connection^.mustsend := TRUE; tk_wake(global^.TCPSend); END; { This routine forms the main body of the TCP data sending task. This task is awakened for one of two reasons: someone has data to send, or a resend timeout has occurred and a retransmission is called for. This routine in either case finishes filling in the header of the output packet, and calls in_write to send it to the net. } PROCEDURE tcp_send(NoArg:PTR); LABEL 666; VAR sndlen: Integer; { bytes to send } Fake:Boolean; err:Integer; connection:Ref_tcconn; BEGIN tk_block; { Forget the initial wakeup } { track through the connection queue looking for data to send } 666: while true do begin connection := global^.tcpconns^.qHead; while connection <> NIL do if connection^.mustsend then leave else connection := connection^.tc_next; if connection = NIL then tk_block else leave; END; WITH connection^,global^ do begin connection^.mustsend := FALSE; if (conn_state = CLOSED) THEN GOTO 666; { If this is not a timeout, clear the resend timer } if newsend THEN { if there is new data to send } BEGIN Fake := tm_clear(tcptm); tm_tset(retry_time, @tmhnd, POINTER(ORD4(connection)), tcptm); END; if resend <> 0 THEN { if resend timer has gone off } BEGIN tcpresend := tcpresend + 1; if resend >= 12 THEN BEGIN if (NOT_YET(conn_state, ESTAB)) THEN begin CALL(tc_tfcn); { should be Boolean function } CALL(tc_cfcn); tcp_free(connection); goto 666; end else BEGIN NotResponding(StrCvt('TCP')); CALL(tc_cfcn); tcp_free(connection); goto 666; END; END else if (NOT_YET(conn_state, ESTAB)) THEN CALL(tc_rfcn); Fake := tm_clear(tcptm); tm_tset(retry_time,@tmhnd, POINTER(ORD4(connection)), tcptm) END; { if resend <> 0 then } { Calculate the number of bytes to send, keeping in mind the foreign host's window. If the connection isn't yet open, send no data at all. } IF NOT_YET(conn_state,ESTAB) THEN sndlen := 0 ELSE sndlen := odlen; { Finish filling in the TCP header } otp^.tc_thl := sizeof(tcp) div 4; ophp^.tp_len := sndlen + sizeof(tcp); otp^.tc_cksum := cksum(POINTER(ORD4(ophp)),sizeof(tcpph) div 2); otp^.tc_cksum := BitNOT(cksum(POINTER(ORD4(otp)), (sndlen+sizeof(tcp)+1) div 2)); {$IFC DEBUG} Write('Sending '); Print_TCP(otp); {$ENDC} { Send it } err := in_write(tcpfd, opbi, sndlen + sizeof(tcp), ForeignHost); if err <= 0 then begin Error(StrCvt('TELNET: Destination unreachable')); CALL(tc_cfcn); tcp_free(connection); goto 666; end; ack_time := TickCount + ACKDALLY; tcppsnt := tcppsnt + 1; tcpbsnt := otp^.tc_seq + sndlen; tcprack := tcpbrcv; if otp^.tc_rst THEN BEGIN { were we resetting? } cleanup(StrCvt('aborted')); CALL(tc_cfcn); tcp_free(connection); END else if conn_state = TIMEWAIT THEN { if we sent the last ack } BEGIN CALL(tc_cfcn); tcp_free(connection); END; end; { with } GOTO 666; END; { tcp_send } { Stuff a character into the send buffer for transmission, but do not wake up the TCP sending task. This assumes that more data will immediately follow. } FUNCTION tc_put(connection:Ref_tcconn; c: char): Boolean; VAR RawPtr: PTR; tmp: byte; BEGIN WITH connection^ do begin if NOT blk_inpt THEN BEGIN if (odlen >= MAXBUF) THEN BEGIN tc_put := TRUE; EXIT(tc_put); END else BEGIN RawPtr := POINTER(ORD4(odp)+odlen); RawPtr^ := byte(c); odlen := odlen + 1; RawPtr := POINTER(ORD4(odp)+odlen); RawPtr^ := 0; tc_put := FALSE; EXIT(tc_put); END END else BEGIN tc_put := TRUE; EXIT(tc_put); END; end;{with} END; { tc_put } { Stuff a character into the send buffer for transmission, and wake up the TCP sender task to send it. } FUNCTION tc_fput(connection:Ref_tcconn; c: char): Boolean; VAR RawPtr: PTR; BEGIN WITH connection^ do begin if(odlen >= MAXBUF) THEN BEGIN tc_fput := TRUE; EXIT(tc_fput); END; if NOT blk_inpt THEN BEGIN RawPtr := POINTER(ORD4(odp)+odlen); RawPtr^ := byte(c); odlen := odlen + 1; RawPtr := POINTER(ORD4(odp)+odlen); RawPtr^ := 0; END else BEGIN tc_fput := TRUE; EXIT(tc_fput); END; newsend := TRUE; send_wake(connection); tc_fput := FALSE; end;{with} END; { tc_fput } { Indicate the presence of urgent data. Just sets the urgent pointer to the current data length and wakes up the sender. } PROCEDURE tcpurgent(connection:Ref_tcconn); BEGIN WITH connection^ do begin otp^.tc_urg := odlen; otp^.tc_furg := true; send_wake(connection); end;{with} END; { tcpurgent } PROCEDURE TCP_Win(connection:Ref_tcconn; win:Integer; lowwin: Integer); BEGIN WITH connection^ do begin if (win<1) OR (win>TCPMAXWIND) OR (lowwin<1) OR (lowwin>TCPMAXWIND) THEN BEGIN TelWin := TCPWINDOW; TelLowWin := TCPLOWIND; END else BEGIN TelWin := win; TelLowWin := lowwin; END; end;{with} END; { Actively open a tcp connection to foreign host fh on foreign socket fs. Get a unique local socket to open the connection on. Returns FALSE if unable to open an internet connection with the specified hosts and sockets, or TRUE otherwise. Note that this routine does not wait until the connection is actually opened before returning. Instead, the user open function specified as ofcn in the call to tcp_init (which must precede this call) will be called when the connection is successfully opened. } FUNCTION tcp_open(fh: Ref_in_name; fs: unsigned; ls: unsigned; win:Integer; lowwin: Integer; ofcn,infcn,yldfcn,cfcn,tmofcn,rsdfcn,buff:ProcPtr):Ref_tcconn; { *fh: in_name;} { foreign host address } { fs:unsigned;} { foreign socket } { ls:unsigned;} { local socket } { int win;} { window to advertise } { int lowwin;} { low water mark for window } { int (*ofcn)();} { user fcn to call on open done } { int (*infcn)();} { user fcn to process input data } { int (*yldfcn)();} { predicate set when user needs to run} { int (*cfcn)();} { user fcn to call on close done } { int (*tmofcn)();} { user fcn to call on icp timeout } { int (*rsdfcn)();} { user icp resend function } { int (*buff)();} { user output buffer room upcall } VAR Dummy:Boolean; connection:Ref_tcconn; BEGIN connection := tcp_alloc; if connection = NIL then begin CantAlloc(StrCvt('TCP'),StrCvt('connection')); tcp_open := NIL; exit(tcp_open); end; WITH connection^ do begin tc_ofcn := ofcn; { save user fcn addresses } tc_dispose := infcn; tc_yield := yldfcn; tc_cfcn := cfcn; tc_tfcn := tmofcn; tc_rfcn := rsdfcn; tc_buff := buff; ForeignHost := fh^; DestPort := fs; SourcePort := ls; if ls = 0 THEN BEGIN SourcePort := TickCount; if(SourcePort < 1000) THEN SourcePort := SourcePort + 1000; END; TCP_Win(connection,win,lowwin); conn_state := SYNSENT; { syn-sent } ophp^.tp_src := in_mymach(fh^); ophp^.tp_dst := fh^; otp^.tc_srcp := ls; otp^.tc_dstp := fs; otp^.tc_win := TelWin; { set resend timer } tm_tset(retry_time, @tmhnd, POINTER(ORD4(connection)), tcptm); newsend := TRUE; send_wake(connection); end;{with} tcp_open := connection; END; { tcp_open } { Passive open; new in Mac version } {$IFC LISTEN} FUNCTION tcp_listen(ls: unsigned; win:Integer; lowwin: Integer; ofcn,infcn,yldfcn,cfcn,tmofcn,rsdfcn,buff:ProcPtr):Ref_tcconn; { ls:unsigned;} { local socket } { int win;} { window to advertise } { int lowwin;} { low water mark for window } { int (*ofcn)();} { user fcn to call on open done } { int (*infcn)();} { user fcn to process input data } { int (*yldfcn)();} { predicate set when user needs to run} { int (*cfcn)();} { user fcn to call on close done } { int (*tmofcn)();} { user fcn to call on icp timeout } { int (*rsdfcn)();} { user icp resend function } { int (*buff)();} { user output buffer room upcall } VAR connection:Ref_tcconn; BEGIN connection := tcp_alloc; if connection = NIL then begin CantAlloc(StrCvt('TCP'),StrCvt('connection')); tcp_listen := NIL; exit(tcp_listen); end; WITH connection^ do begin tc_ofcn := ofcn; { save user fcn addresses } tc_dispose := infcn; tc_yield := yldfcn; tc_cfcn := cfcn; tc_tfcn := tmofcn; tc_rfcn := rsdfcn; tc_buff := buff; DestPort := 0; SourcePort := ls; TCP_Win(connection,win,lowwin); otp^.tc_win := TelWin; conn_state := LISTEN; end;{with} tcp_listen := connection; END; { tcp_listen } {$ENDC} { Display some tcp statistics and a few lines of unacked data. Should be revised and integrated in with the normal logging system. } { Changed this to go through the terminal emulator } PROCEDURE tc_status(connection:Ref_tcconn); LABEL 777; VAR i:Integer; { loop index } crseen:Integer; RawPtr:PTR; BEGIN if connection = NIL then exit(tc_status); WITH connection^,global^ do begin crseen := 0; EmStr('Connection State: '); CASE (conn_state) OF CLOSED: EmLn('Closed'); SYNSENT: EmLn('Trying to Open'); SYNRCVD: EmLn('Being Opened'); ESTAB: EmLn('Established'); {$IFC LISTEN} LISTEN: EmLn('Listening for connections'); {$ENDC} OTHERWISE BEGIN EmStr('('); NumToStr(conn_state,Msg); EmStr(Msg); EmLn(') Closing'); END; END; EmStr('Packets Sent: '); NumToStr(tcppsnt,Msg);EmStr(Msg); EmStr('; Packets Received: '); NumToStr(tcpprcv,Msg);EmLn(Msg); EmStr('Bytes Sent: '); NumToStr(tcpbsnt,Msg);EmStr(Msg); EmStr(' (Acked: '); NumToStr(otp^.tc_seq,Msg);EmStr(Msg); EmLn(')'); EmStr('Bytes Received: '); NumToStr(tcpbrcv,Msg);EmStr(Msg); EmStr(' (Acked: '); NumToStr(tcprack,Msg);EmStr(Msg); EmLn(')'); EmStr('Bad TCP xsums: '); NumToStr(bd_chk,Msg);EmStr(Msg); EmStr('; Window ignored: '); NumToStr(ign_win,Msg);EmLn(Msg); EmStr('Packets not for me: '); NumToStr(tcpsock,Msg);EmStr(Msg); EmStr('; Resends: '); NumToStr(tcpresend,Msg);EmStr(Msg); EmStr('; Rereceived: '); NumToStr(tcprercv,Msg);EmLn(Msg); EmStr('Local Win: '); NumToStr(otp^.tc_win,Msg);EmStr(Msg); EmStr('; Local Low Win: '); NumToStr(TelLowWin,Msg);EmStr(Msg); EmStr('; Foreign Win: '); NumToStr(frn_win,Msg);EmLn(Msg); EmStr('Ack #: '); NumToStr(otp^.tc_ack,Msg);EmStr(Msg); EmStr(', Seq #: '); NumToStr(otp^.tc_seq,Msg);EmLn(Msg); EmStr('Output Flags:'); if(otp^.tc_syn) THEN EmStr(' SYN'); if(otp^.tc_fack) THEN EmStr(' ACK'); if(otp^.tc_psh) THEN EmStr(' PSH'); if(otp^.tc_furg) THEN EmStr(' URG'); if(otp^.tc_fin) THEN EmStr(' FIN'); if(otp^.tc_rst) THEN EmStr(' RST'); EmLn('.'); if(odlen <> 0) THEN EmLn('Output data:') else EXIT(tc_status); i:=0; while(true) DO BEGIN RawPtr := POINTER(ORD4(odp)+i); if RawPtr^ = $d THEN crseen := crseen + 1; if (RawPtr^ >= 32) AND (RawPtr^ < 127) then Em(chr(RawPtr^)) else if RawPtr^ = $d then EmLn('') else EmStr('.'); if crseen > 3 THEN GOTO 777 ELSE BEGIN i := i + 1; if (i > 100) OR (i > odlen) THEN GOTO 777 END; END; 777: EmLn(''); if i <= odlen THEN BEGIN EmLn('***MORE DATA***'); END; end;{with} em_flush; END; { tcp_status } { expedite (resend and push) a packet } PROCEDURE tcp_ex(connection:Ref_tcconn); BEGIN send_wake(connection); connection^.otp^.tc_psh := true; END; PROCEDURE tcp_ack(connection:Ref_tcconn); BEGIN send_wake(connection); END; { tcp_ack} { Handle a timer upcall. } PROCEDURE tmhnd(connection:Ref_tcconn); BEGIN connection^.resend := connection^.resend + 1; send_wake(connection); END; { tmhnd } FUNCTION tcp_sock: unsigned; VAR temp: LongInt; BEGIN temp := TickCount; temp := BitAnd(temp,$0000ffff); if (temp < 1000) THEN temp := temp + 1000; tcp_sock := temp; END; { tcp_sock} {$IFC SAVEREST} VAR SeqNum:Integer; AckNum:Integer; { restore a suspended tcp connection } PROCEDURE tcp_restore; BEGIN conn_state := ESTAB; tcpfd := in_open(TCPPROT, @tcp_rcv); if (tcpfd = NIL) THEN BEGIN CantConnect('tcp_restore','Internet'); CALL(tc_cfcn); tcp_free(connection); EXIT(tcp_restore); END; ophp^.tp_src := in_mymach(ForeignHost); ophp^.tp_dst := ForeignHost; otp^.tc_srcp := SourcePort; otp^.tc_dstp := DestPort; otp^.tc_seq := SeqNum; otp^.tc_ack := AckNum-1; otp^.tc_win := TelWin; otp^.tc_bits := 0; newsend := TRUE; send_wake(connection); Write('foreign host '); out_inaddr(ForeignHost); WriteLn(', foreign port ',DestPort:1,', local port ',SourcePort:1); WriteLn('ack # is ',AckNum:1,' and seq # is ',SeqNum:1); CALL(tc_ofcn); END; { tcp_restore } FUNCTION tcp_save { : Boolean } ; VAR OSStatus: OSErr; FRN: Integer; RLength: LongInt; CR:CustRecord; BEGIN OSStatus := FSOpen(CFileName, {Current Vol} 0, FRN); if( OSStatus <> noErr ) THEN tcp_save := FALSE else BEGIN RLength := sizeof(CustRecord); CR.SeqNum := otp^.tc_seq; CR.AckNum := otp^.tc_ack; CR.LocalIPAddr := LocalIPAddress; CR.GateWIPAddr := GWIPAddress; CR.NameServer := NSIPAddress[0]; CR.TimeServer := TSIPAddress[0]; CR.UseAB := Use_AB; CR.UserName := User_Name; CR.SourcePort := SourcePort; CR.DestPort := DestPort; CR.TelLowWin := TelLowWin; CR.ForeignHost := ForeignHost; { Get to the start of the file } OSStatus := SetFPos(FRN,fsFromStart,0); IF OSStatus = noErr THEN OSStatus := FSWrite(FRN, RLength, @CR); OSStatus := FSClose(FRN); tcp_save := TRUE END END; { tcp_save } {$ENDC} END. !E!O!F! exit -=- Tim Maroney, Professional Heretic, CMU Center for Art and Technology tim@k.cs.cmu.edu | uucp: {seismo,decwrl,ucbvax,etc.}!k.cs.cmu.edu!tim CompuServe: 74176,1360 | God is not dead; he just smells funny.