home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / FetchNews 1.0.0b / source / TCPStuff.unit < prev    next >
Encoding:
Text File  |  1993-02-23  |  22.9 KB  |  787 lines  |  [TEXT/PJMM]

  1. unit TCPStuff;
  2.  
  3. { TCPStuff © Peter Lewis, Oct 1991 }
  4. { This source is Freeware }
  5.  
  6. interface
  7.  
  8.     uses
  9.         TCPTypes;
  10.  
  11.     const
  12.         Minimum_TCPBUFFERSIZE = 4096;
  13.         Default_TCPBUFFERSIZE = longInt(6) * 1024;
  14.     { Amount of space to allocate for each TCP connection }
  15.         INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  16.         control_block_max = 260;
  17.         tooManyControlBlocks = -23098;
  18.  
  19.     type
  20.         OSErrPtr = ^OSErr;
  21.  
  22. { TCP connection description: }
  23.         TCPConnectionType = record
  24.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  25.                 stream: StreamPtr;
  26.                 closedone: boolean;
  27.                 laststate: integer;
  28.                 asends, asendcompletes: longInt;
  29.                 closeuserptr: OSErrPtr;
  30.                 incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  31.                 incomingSize: longInt;                        { Number of bytes left in inBuf. }
  32.                 buffer: ptr;        { connection buffer. }
  33.                 inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  34.             end;
  35.         TCPConnectionPtr = ^TCPConnectionType;
  36.  
  37.         MyControlBlock = record
  38.                 tcp: TCPControlBlock;
  39.                 inuse: boolean;
  40.                 userptr: OSErrPtr;
  41.                 proc: procPtr;
  42.                 tcpc: TCPConnectionPtr;
  43.             end;
  44.         MyControlBlockPtr = ^MyControlBlock;
  45.  
  46.  
  47.         TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  48.             T_Closing, T_PleaseClose, T_Unknown);
  49.  
  50.     function TCPNameToAddr (var hostName: str255; timeout: longInt): longInt;
  51.     function TCPOpenResolver (var dataptr: ptr): OSErr;
  52.     function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  53.     procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  54.     function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  55.     procedure TCPCloseResolver (dataptr: ptr);
  56.  
  57.     function C2PStr (s: stringPtr): stringPtr;
  58.     procedure SanitizeHostName (var s: str255);
  59.  
  60.     function TCPInit: OSErr;
  61.     procedure TCPFinish;
  62.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  63.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  64.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  65.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  66.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  67.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  68.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  69.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  70.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  71.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  72.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  73.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  74.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  75. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  76.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  77.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  78.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  79.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  80.                                     var gottermchar: boolean): OSErr;
  81.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  82.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  83.  
  84.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  85.  
  86. implementation
  87.  
  88.     const
  89.         MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  90.         dispose_block_max = 100;
  91.  
  92.     type
  93.         MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  94.  
  95.     var
  96.         driver_refnum: integer;
  97.         controlblocks: MyControlBlockArray;
  98.         disposeblocks: array[1..dispose_block_max] of ptr;
  99.  
  100.     procedure SanitizeHostName (var s: str255);
  101.         var
  102.             dummysp: stringPtr;
  103.     begin
  104.         dummysp := C2PStr(@s);
  105. {$PUSH}
  106. {$R-}
  107.         if s[Length(s)] = '.' then
  108.             s[0] := chr(Length(s) - 1);
  109. {$POP}
  110.     end;
  111.  
  112.     function GetA6: ptr;
  113.     inline
  114.         $2F4E, $0000;
  115.  
  116.     procedure CallCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  117.     inline
  118.         $205F, $4E90;
  119.  
  120. {$PUSH}
  121. {$D-}
  122.     procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  123.         type
  124.             stackframe = packed record
  125.                     frameptr: ptr;
  126.                     returnptr: ptr;
  127.                     paramblockptr: MyControlBlockPtr;
  128.                 end;
  129.             stackframeptr = ^stackframe;
  130.         var
  131.             a6: stackframeptr;
  132.             cbp: MyControlBlockPtr;
  133.     begin
  134.         a6 := stackframeptr(GetA6);
  135.         cbp := a6^.paramblockptr;
  136.         with cbp^ do begin
  137.             if userptr <> nil then
  138.                 userptr^ := cbp^.tcp.ioResult;
  139.             inuse := false;
  140.             if proc <> nil then
  141.                 CallCompletion(cbp, proc);
  142.         end;
  143.     end;
  144.  
  145.     procedure ZotBlocks;
  146.         var
  147.             i: integer;
  148.     begin
  149.         for i := 1 to dispose_block_max do begin
  150.             if disposeblocks[i] <> nil then begin
  151.                 DisposePtr(disposeblocks[i]);
  152.                 disposeblocks[i] := nil;
  153.             end;
  154.         end;
  155.     end;
  156.  
  157.     procedure AddBlock (p: univ ptr);
  158. { Called at interupt level }
  159. { Must work even while ZotBlocks is in progress }
  160.         var
  161.             i: integer;
  162.     begin
  163.         for i := 1 to dispose_block_max do begin
  164.             if disposeblocks[i] = nil then begin
  165.                 disposeblocks[i] := p;
  166.                 leave;
  167.             end;
  168.         end;
  169.     end;
  170.  
  171.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  172.     { Zero out the control block parameters. }
  173.         var
  174.             i: integer;
  175.             p: longInt;
  176.     begin
  177.         ZotBlocks;
  178.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  179.             ptr(p)^ := 0;
  180.         cb.tcpStream := stream;
  181.         cb.ioCRefNum := driver_refnum;
  182.         cb.csCode := call;
  183.     end;
  184.  
  185.     function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  186. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  187.         var
  188.             i: integer;
  189.     begin
  190.         i := 1;
  191.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  192.             i := i + 1;
  193.         cbp := controlblocks[i];
  194.         if cbp = nil then begin
  195.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  196.             if cbp <> nil then begin
  197.                 cbp^.inuse := false;
  198.                 controlblocks[i] := cbp;
  199.             end;
  200.         end;
  201.         if (cbp <> nil) & not cbp^.inuse then begin
  202.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  203.             cbp^.tcp.ioCompletion := @IOCompletion;
  204.             cbp^.inuse := true;
  205.             cbp^.userptr := userptr;
  206.             cbp^.tcpc := tcpc;
  207.             cbp^.proc := proc;
  208.             if userptr <> nil then
  209.                 userptr^ := inprogress;
  210.             GetCB := noErr;
  211.         end
  212.         else begin
  213.             cbp := nil;
  214.             GetCB := memFullErr;
  215.         end;
  216.     end;
  217.  
  218.     procedure FreeCB (var cbp: MyControlBlockPtr);
  219.     begin
  220.         if cbp <> nil then
  221.             cbp^.inuse := false;
  222.         cbp := nil;
  223.     end;
  224. {$POP}
  225.  
  226. {$S Init}
  227.     function TCPInit: OSErr;
  228.         var
  229.             oe: OSErr;
  230.             i: integer;
  231.     begin
  232.         oe := OpenDriver('.IPP', driver_refnum);
  233.         for i := 1 to control_block_max do
  234.             controlblocks[i] := nil;
  235.         TCPInit := oe;
  236.     end;
  237.  
  238. {$S Term}
  239.     procedure TCPFinish;
  240.         var
  241.             i: integer;
  242.     begin
  243.         for i := 1 to control_block_max do
  244.             if controlblocks[i] <> nil then begin
  245.                 DisposPtr(ptr(controlblocks[i]));
  246.                 controlblocks[i] := nil;
  247.             end;
  248.     end;
  249.  
  250. {$S}
  251.     procedure DestroyConnection (var connection: TCPConnectionPtr);
  252.     begin
  253.         connection^.magic := '????';
  254.         if connection^.buffer <> nil then
  255.             DisposPtr(ptr(connection^.buffer));
  256.         DisposPtr(Ptr(connection));
  257.         connection := nil;
  258.     end;
  259.  
  260.     function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  261.     begin
  262.         if connection = nil then
  263.             ValidateConnection := connectionDoesntExist
  264.         else if connection^.magic <> MAGICNUMBER then
  265.             ValidateConnection := connectionDoesntExist
  266.         else
  267.             ValidateConnection := noErr;
  268.     end;
  269.  
  270.     function PBControlSync (var cb: TCPControlBlock): OSErr;
  271.     begin
  272.         PBControlSync := PBControl(@cb, false);
  273.     end;
  274.  
  275. {$PUSH}
  276. {$D-}
  277.     function PBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  278.         var
  279.             oe: OSErr;
  280.     begin
  281.         oe := PBControl(ParmBlkPtr(cbp), true);
  282.         if oe <> noErr then
  283.             FreeCB(cbp);
  284.         PBControlAsync := oe;
  285.     end;
  286. {$POP}
  287.  
  288.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  289.         var
  290.             cb: TCPControlBlock;
  291.             oe: OSErr;
  292.     begin
  293.         ZeroCB(cb, nil, TCPcsGetMyIP);
  294.         oe := PBControlSync(cb);
  295.         myIP := cb.getmyip.ourAddress;
  296.         TCPGetMyIPAddr := oe;
  297.     end;
  298.  
  299.     procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  300.     begin
  301.         if userptr <> nil then begin
  302.             if oe <> noErr then
  303.                 userptr^ := oe;
  304.         end;
  305.     end;
  306.  
  307.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  308.         var
  309.             oe: OSErr;
  310.     begin
  311.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  312.         if connection = nil then
  313.             oe := memFullErr
  314.         else begin
  315.             oe := noErr;
  316.             with connection^ do begin
  317.                 buffer := nil;
  318.                 magic := MAGICNUMBER;
  319.                 asends := 0;
  320.                 asendcompletes := 0;
  321.                 closedone := false;
  322.                 incomingSize := 0;
  323.                 stream := strm;
  324.             end;
  325.         end;
  326.         if (oe <> noErr) and (connection <> nil) then
  327.             DestroyConnection(connection);
  328.         TCPCreateConnectionForStream := oe;
  329.     end;
  330.  
  331.     function CreateStream (var connection: TCPConnectionPtr; buffersize: longInt): OSErr;
  332.         var
  333.             oe: OSErr;
  334.             cb: TCPControlBlock;
  335.     begin
  336.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  337.         if connection = nil then
  338.             oe := memFullErr
  339.         else
  340.             with connection^ do begin
  341.                 buffer := NewPtr(buffersize);
  342.                 if buffer = nil then begin
  343.                     oe := memFullErr;
  344.                     DisposPtr(ptr(connection));
  345.                     connection := nil;
  346.                 end
  347.                 else begin
  348.                     magic := MAGICNUMBER;
  349.                     asends := 0;
  350.                     asendcompletes := 0;
  351.                     closedone := false;
  352.                     incomingSize := 0;
  353.                     ZeroCB(cb, nil, TCPcsCreate);
  354.                     cb.create.rcvBuff := buffer;
  355.                     cb.create.rcvBuffLen := buffersize;
  356.                     oe := PBControlSync(cb);
  357.                     stream := cb.tcpStream;
  358.                 end;
  359.             end;
  360.         if (oe <> noErr) and (connection <> nil) then
  361.             DestroyConnection(connection);
  362.         CreateStream := oe;
  363.     end;
  364.  
  365.     function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  366.         var
  367.             oe, ooe: OSErr;
  368.             cbp: MyControlBlockPtr;
  369.             cb: TCPControlBlock;
  370.     begin
  371.         oe := CreateStream(connection, buffersize);
  372.         if oe = noErr then begin
  373.             with connection^ do begin
  374.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  375.                 if oe = noErr then begin
  376.                     cbp^.tcp.open.localPort := localPort;
  377.                     cbp^.tcp.open.remoteHost := remoteIP;
  378.                     cbp^.tcp.open.remotePort := remoteport;
  379.                     oe := PBControlAsync(cbp);
  380.                 end;
  381.                 if oe <> noErr then begin
  382.                     ZeroCB(cb, stream, TCPcsRelease);
  383.                     ooe := PBControlSync(cb);
  384.                     DestroyConnection(connection);
  385.                 end;
  386.             end;
  387.         end;
  388.         SetUserPtr(userptr, oe);
  389.         PAOpen := oe;
  390.     end;
  391.  
  392. { Open a connection to another machine }
  393.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  394.     begin
  395.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  396.     end;
  397.  
  398. { Open a socket on this machine, to wait for a connection }
  399.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  400.     begin
  401.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  402.     end;
  403.  
  404.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  405. { Return readCount characters from the TCP connection. }
  406. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  407.         var
  408.             cb: TCPControlBlock;
  409.             oe: OSErr;
  410.     begin
  411.         repeat
  412.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  413.             cb.receive.rcvBuff := returnPtr;
  414.             cb.receive.rcvBuffLength := readCount;
  415.             oe := PBControlSync(cb);
  416.             longInt(returnPtr) := longInt(returnPtr) + cb.receive.rcvBuffLength;
  417.             readCount := readCount - cb.receive.rcvBuffLength;
  418.         until (oe <> noErr) or (readCount = 0);
  419.         TCPRawReceiveChars := oe;
  420.     end;
  421.  
  422. { Return readCount characters from the TCP connection.}
  423.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  424.         var
  425.             readCountStr: Str255;
  426.             l: longInt;
  427.             p: Ptr;
  428.             oe: OSErr;
  429.             cb: TCPControlBlock;
  430.     begin
  431.         oe := ValidateConnection(connection);
  432.         if oe = noErr then
  433.             if readCount < 0 then
  434.                 oe := invalidLength
  435.             else if readCount > 0 then begin
  436.                 p := returnPtr;
  437.                 with connection^ do
  438.                     if incomingSize > 0 then begin
  439.             { Read as much as there is or as much as we need, whichever is less. }
  440.                         if readCount < incomingSize then
  441.                             l := readCount
  442.                         else
  443.                             l := incomingSize;
  444.                         BlockMove(incomingPtr, p, l);
  445.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  446.                         incomingSize := incomingSize - l;
  447.                         p := Ptr(ord4(p) + l);
  448.                         readCount := readCount - l;
  449.                     end;
  450.                 { If there's more needed, then read it from the connection. }
  451.                 if readCount > 0 then begin
  452.                         { Issue a read and wait until it all arrives). }
  453.                     oe := TCPRawReceiveChars(connection, p, readCount);
  454.                 end;
  455.             end;
  456.         TCPReceiveChars := oe;
  457.     end;
  458.  
  459.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  460.         { Return the next byte in the buffer, reading more in if necessary. }
  461.         var
  462.             waitUntil: longInt;
  463.             readIn: longInt;
  464.             oe: OSErr;
  465.             cb: TCPControlBlock;
  466.     begin
  467.         oe := ValidateConnection(connection);
  468.         if oe = noErr then
  469.             with connection^ do begin            { Check if we need to read in more bytes. }
  470.                 if incomingSize = 0 then begin
  471.                     if (timeout = 0) and (TCPCharsAvailable(connection) = 0) then
  472.                         oe := commandTimeout
  473.                     else begin
  474.                         waitUntil := TickCount + timeout;
  475.     { keep on trying to read until we get at least one, or the time-out happens. }
  476.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  477.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  478.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  479.                                 if readIn > INCOMINGBUFSIZE then
  480.                                     readIn := INCOMINGBUFSIZE;
  481.                         { Issue the read. }
  482.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  483.                                 if oe = noErr then begin
  484.                                     incomingSize := readIn;
  485.                                     incomingPtr := @inBuf;
  486.                                 end;
  487.                             end        { If not, do another round or get out, depending on the timeout condition. }
  488.                             else if TickCount > waitUntil then begin
  489.                                 oe := commandTimeOut;
  490.                             end;
  491.                         end;
  492.                     end;
  493.                 end;
  494.                 { Get the byte to return. }
  495.                 if incomingSize > 0 then begin
  496.                     b := incomingPtr^;
  497.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  498.                     incomingSize := incomingSize - 1;
  499.                 end
  500.                 else
  501.                     b := 0;
  502.             end;
  503.         TCPReadByte := oe;
  504.     end;
  505.  
  506. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  507. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  508. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  509. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  510. { will read the entire buffer, and any characters that arrive before a timeout }
  511.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  512.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  513.                                     var gottermchar: boolean): OSErr;
  514.         var
  515.             oe: OSErr;
  516.             inChar: SignedByte;
  517.             p: Ptr;
  518.     begin
  519.         oe := ValidateConnection(connection);
  520.         gottermchar := false;
  521.         if oe = noErr then begin
  522. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  523.             while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  524.                 oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  525.                 if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  526.                     p := Ptr(ord4(readPtr) + readPos);
  527.                     p^ := inChar;
  528.                     readPos := readPos + 1;
  529.                     gottermchar := inChar = termChar;
  530.                 end;
  531.             end;
  532.             if oe = commandTimeOut then
  533.                 oe := noErr;
  534.         end;
  535.         TCPReceiveUpTo := oe;
  536.     end;
  537.  
  538.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  539.         var
  540.             wds: wdsType;
  541.             oe: OSErr;
  542.             cb: TCPControlBlock;
  543.             p: ptr;
  544.     begin
  545.         oe := ValidateConnection(connection);
  546.         if oe = nOErr then
  547.             if writeCount > 0 then begin
  548.                 wds.buffer := writePtr;
  549.                 wds.size := writeCount;
  550.                 wds.term := 0;
  551.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  552.                 cb.send.wds := @wds;
  553.                 cb.send.pushFalg := ord(push);
  554.                 oe := PBControlSync(cb);
  555.             end
  556.             else if writeCount < 0 then
  557.                 oe := InvalidLength;
  558.         TCPSend := oe;
  559.     end;
  560.  
  561. {$PUSH}
  562. {$D-}
  563.     procedure TCPSendComplete (cbp: MyControlBlockPtr);
  564.         var
  565.             oe: OSErr;
  566.     begin
  567.         AddBlock(cbp^.tcp.send.wds);
  568.         with cbp^.tcpc^ do begin
  569.             asendcompletes := asendcompletes + 1;
  570.             if (asendcompletes = asends) and closedone then begin
  571.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  572. { GetCB won't NewPtr because the completion has just released a block }
  573.                 if oe = noErr then begin
  574.                     oe := PBControlAsync(cbp);
  575.                 end;
  576.             end;
  577.         end;
  578.     end;
  579. {$POP}
  580.  
  581.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  582.         type
  583.             myblock = record
  584.                     wds: wdsType;
  585.                     data: array[0..100] of byte;
  586.                 end;
  587.             myblockptr = ^myblock;
  588.         var
  589.             oe: OSErr;
  590.             cbp: MyControlBlockPtr;
  591.             p: myblockptr;
  592.     begin
  593.         oe := ValidateConnection(connection);
  594.         if oe = nOErr then
  595.             if writeCount > 0 then begin
  596.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  597.                 if p = nil then
  598.                     oe := memFullErr
  599.                 else begin
  600.                     p^.wds.buffer := @p^.data;
  601.                     p^.wds.size := writeCount;
  602.                     p^.wds.term := 0;
  603.                     with p^.wds do
  604.                         BlockMove(writePtr, buffer, size);
  605.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  606.                     cbp^.tcp.send.wds := POINTER(p);
  607.                     cbp^.tcp.send.pushFalg := ord(push);
  608.                     with connection^ do
  609.                         asends := asends + 1;
  610.                     oe := PBControlAsync(cbp);
  611.                     if oe <> noErr then
  612.                         DisposPtr(ptr(p));
  613.                 end;
  614.             end
  615.             else if writeCount < 0 then
  616.                 oe := InvalidLength;
  617.         TCPSendAsync := oe;
  618.     end;
  619.  
  620.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  621.         var
  622.             oe: OSErr;
  623.             cbp: MyControlBlockPtr;
  624.     begin
  625.         oe := ValidateConnection(connection);
  626.         if oe = noErr then
  627.             with connection^ do begin
  628.                 closeuserptr := userptr;
  629.                 if userptr <> nil then
  630.                     userptr^ := inProgress;
  631.                 closedone := true;
  632.                 if asends = asendcompletes then begin
  633.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  634.                     if oe = noErr then begin
  635.                         oe := PBControlAsync(cbp);
  636.                     end;
  637.                 end;
  638.             end;
  639.         SetUserPtr(userptr, oe);
  640.         TCPClose := oe;
  641.     end;
  642.  
  643.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  644.         var
  645.             oe: OSErr;
  646.             cb: TCPControlBlock;
  647.     begin
  648.         oe := ValidateConnection(connection);
  649.         if oe = noErr then begin
  650.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  651.             oe := PBControlSync(cb);
  652.         end;
  653.         TCPAbort := oe;
  654.     end;
  655.  
  656. { Release the TCP stream, including the buffer.}
  657.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  658.         var
  659.             oe: OSErr;
  660.             cb: TCPControlBlock;
  661.     begin
  662.         oe := ValidateConnection(connection);
  663.         if oe = noErr then begin
  664.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  665.             oe := PBControlSync(cb);
  666.             DestroyConnection(connection);
  667.         end;
  668.         TCPRelease := oe;
  669.     end;
  670.  
  671. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  672.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  673.         var
  674.             cb: TCPControlBlock;
  675.             oe: OSErr;
  676.     begin
  677.         oe := ValidateConnection(connection);
  678.         localhost := 0;
  679.         localport := 0;
  680.         remotehost := 0;
  681.         remoteport := 0;
  682.         available := 0;
  683.         if oe <> noErr then begin
  684.             state := 99; { Error -> Closed }
  685.         end
  686.         else begin
  687.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  688.             if PBControlSync(cb) <> noErr then begin
  689.                 state := 99; { Closed }
  690.             end
  691.             else begin
  692.                 state := cb.status.connectionState;
  693.                 connection^.laststate := state;
  694.                 localhost := cb.status.localhost;
  695.                 localport := cb.status.localport;
  696.                 remotehost := cb.status.remotehost;
  697.                 remoteport := cb.status.remoteport;
  698.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  699.             end;
  700.         end;
  701.     end;
  702.  
  703. { Return the state of the TCP connection.}
  704.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  705.         var
  706.             state: integer;
  707.             localhost: longInt;
  708.             localport: integer;
  709.             remotehost: longInt;
  710.             remoteport: integer;
  711.             available: longInt;
  712.     begin
  713.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  714.         case state of
  715.             0: 
  716.                 TCPState := T_Closed;
  717.             2: 
  718.                 TCPState := T_Listening;
  719.             4, 6: 
  720.                 TCPState := T_Opening;
  721.             8: 
  722.                 TCPState := T_Established;
  723.             10, 12, 16, 18, 20: 
  724.                 TCPState := T_Closing;
  725.             14: 
  726.                 TCPState := T_PleaseClose;
  727.             98: 
  728.                 TCPState := T_WaitingForOpen;
  729.             99: 
  730.                 TCPState := T_Closed;
  731.             otherwise
  732.                 TCPState := T_Unknown;
  733.         end;
  734.     end;
  735.  
  736. {    Return the number of characters available for reading from the TCP connection.}
  737.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  738.         var
  739.             state: integer;
  740.             localhost: longInt;
  741.             localport: integer;
  742.             remotehost: longInt;
  743.             remoteport: integer;
  744.             available: longInt;
  745.     begin
  746.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  747.         TCPCharsAvailable := available;
  748.     end;
  749.  
  750.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  751.         var
  752.             state: integer;
  753.             localhost: longInt;
  754.             localport: integer;
  755.             remotehost: longInt;
  756.             remoteport: integer;
  757.             available: longInt;
  758.     begin
  759.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  760.         TCPLocalPort := localport;
  761.     end;
  762.  
  763.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  764.         var
  765.             buffer: array[0..255] of signedByte;
  766.             f: longInt;
  767.             oe: OSErr;
  768.     begin
  769.         f := TCPCharsAvailable(connection);
  770.         oe := noErr;
  771.         while (f > 0) and (oe = noErr) do begin
  772.             if f > 256 then
  773.                 f := 256;
  774.             oe := TCPReceiveChars(connection, @buffer, f);
  775.             if oe = noErr then
  776.                 f := TCPCharsAvailable(connection);
  777.         end;
  778.         TCPFlush := oe;
  779.     end;
  780.  
  781. end.
  782. function TCPNameToAddr (var hostName: str255; timeout: longInt; var hostFile: str255): longInt;
  783.     function TCPOpenResolver (var hostFile: str255; var dataptr: ptr): OSErr;
  784.         function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  785.             procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  786.                 function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  787.                     procedure TCPCloseResolver (dataptr: ptr);