home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / turbo-tcp-20b4-cpp.hqx / TurboTCP / TurboTCP source / CTCPStream.cp < prev    next >
Encoding:
Text File  |  1994-10-11  |  29.0 KB  |  1,146 lines

  1. /*
  2. ** CTCPStream.cp
  3. **
  4. **    TurboTCP support library
  5. **    TCP stream class
  6. **
  7. **    Copyright © 1993-94, FrostByte Design / Eric Scouten
  8. **
  9. */
  10.  
  11.  
  12. #include "CTCPStream.h"
  13.  
  14. #include "CPtrArray.h"
  15. #include "CTCPAsyncCall.h"
  16. #include "CTCPResolverCall.h"
  17.  
  18.  
  19. #ifndef TCL_NO_TEMPLATES
  20.  
  21.     class CTCPAsyncCallList : public CPtrArray<CTCPAsyncCall> {};
  22.     class CCollaboratorList : public CPtrArray<CCollaborator> {};
  23.     class CTCPStreamList : public CPtrArray<CTCPStream> {};
  24.  
  25.     #pragma template_access public
  26.  
  27.     #pragma template CPtrArray<CTCPAsyncCall>
  28.     #pragma template CPtrArray<CTCPStream>
  29.     #pragma template CPtrArray<CTCPResolverCall>
  30.  
  31.     TCL_DEFINE_CLASS_M1(CPtrArray<CTCPAsyncCall>, CVoidPtrArray);
  32.     TCL_DEFINE_CLASS_M1(CPtrArray<CTCPStream>, CVoidPtrArray);
  33.     TCL_DEFINE_CLASS_M1(CPtrArray<CTCPResolverCall>, CVoidPtrArray);
  34.  
  35.     template <class T>
  36.     CPtrArray<T>::CPtrArray(short theBlockSize)
  37.         : CVoidPtrArray(theBlockSize) { }
  38.     
  39.     template <class T>
  40.     void CPtrArray<T>::DisposeAll() { }                // will never be called for these classes
  41.     
  42.     template <class T>
  43.     void CPtrArray<T>::DisposeItems() { }            // will never be called for these classes
  44.  
  45. #else
  46.     TM_DECLARE_CPtrArray(CTCPAsyncCall)
  47.     TM_DECLARE_CPtrArray(CCollaborator)
  48.     TM_DECLARE_CPtrArray(CTCPStream)
  49.     TM_DECLARE_CPtrArray(CTCPResolverCall)
  50.     TM_DEFINE_CPtrArray(CTCPAsyncCall)
  51.     TM_DEFINE_CPtrArray(CTCPStream)
  52.     TM_DEFINE_CPtrArray(CTCPResolverCall)
  53. #endif
  54.  
  55.  
  56. //    —— UPP for notification procedure (corrects a bug in <TCPPB.h>) ——
  57.  
  58. #if USESROUTINEDESCRIPTORS
  59.  
  60. UniversalProcPtr CTCPStream::notifyProcUPP =
  61.             NewRoutineDescriptor((ProcPtr) CTCPStream::NotifyProc, uppTCPNotifyProcInfo, GetCurrentISA());
  62.  
  63. #endif
  64.  
  65.  
  66. TCL_DEFINE_CLASS_M0(CTCPStream);
  67.  
  68.  
  69. //    —— constructor/destructor ——
  70.  
  71. /*______________________________________________________________________
  72. **
  73. ** constructor
  74. **
  75. **    Create a new TCP stream. Allocates the receive buffer (if there’s enough memory).
  76. **
  77. **    The stream can be configured to automatically receive data. If auto-receive is used,
  78. **    the stream object will automatically issue TCPNoCopyRcv calls as needed and respond to
  79. **    completion notifications when data arrives; data will be returned by the HandleDataArrived
  80. **    method or by the receive bypass procedure (see InstallRcvBypassProc).
  81. **
  82. **    If auto-receive is used, you should not send NoCopyRcv and Rcv messages to this stream.
  83. **    They may conflict with the auto-receive calls. Auto-receive cannot be turned on or off
  84. **    once the stream object is created.
  85. **
  86. **        theEndpoint (CTCPEndpoint&):        endpoint object linked to this stream
  87. **        recBufferSize (long):            size of the receive buffer we need; use 0 or
  88. **                                        less then 4K to get the minimum 4K buffer
  89. **        autoReceiveSize (short):            number of entries in RDS for auto-receive,
  90. **                                        0 to disable autoreceiving
  91. **        autoReceiveNum (short):            number of auto-receive calls to issue at once
  92. **                                        must be at least 1
  93. **
  94. */
  95.  
  96. CTCPStream::CTCPStream(CTCPEndpoint& theEndpoint, long recBufferSize,
  97.     short autoReceiveSize, short autoReceiveNum)
  98.     : itsEndpoint(&theEndpoint), qNotifyEntry(this), qDisposeEntry(this)
  99.  
  100. {
  101.  
  102.     // clear all of our variables
  103.     
  104.     macTCPStream = NULL;
  105.     itsBuffer = NULL;
  106.     itsAsyncCalls = NULL;    
  107.     hasSessionOpen = pendingOpen = pendingClose = remoteClose
  108.         = pendingAbort = pendingNotify = pendingDispose = FALSE;
  109.     
  110.     itsBufferSize = 0L;
  111.     rcvUrgent = FALSE;
  112.     
  113.     itsULPtimeout = 0;
  114.     itsULPaction = 0;
  115.     itsValidityFlag = 0;
  116.     itsCommandTimeoutValue = 0;
  117.     itsTosFlags = 0;
  118.     itsPrecedence = 0;
  119.     itsDontFrag = 0;
  120.     itsTimeToLive = 0;
  121.     itsSecurity = 0;
  122.     itsOptionCnt = 0;
  123.     sendNextUrgent = 0;
  124.     sendNextPush = FALSE;
  125.  
  126.     notifClosing = FALSE;
  127.     notifTimeout = FALSE;
  128.     notifTerminate = FALSE;
  129.     notifDataArrived = FALSE;
  130.     notifUrgent = FALSE;
  131.     notifICMP = FALSE;
  132.     disposeOnTerminate = FALSE;
  133.     receivedClose = FALSE;
  134.     receivedTerminate = FALSE;
  135.     itsAutoReceiveSize = Min(autoReceiveSize, autoReceiveMax);
  136.     itsAutoReceiveNum = Max(autoReceiveNum, 1);
  137.  
  138.     qNotifyEntry.qType = notifyStream;
  139.     qDisposeEntry.qType = disposeStream;
  140.  
  141.     itsAsyncCalls = new CTCPAsyncCallList;
  142.  
  143.  
  144.     // create the receive buffer
  145.     
  146.     if (recBufferSize < minReceiveSize)                    // if caller requested a buffer that is too small,
  147.         recBufferSize = minReceiveSize;                //    beef it up to acceptable size (4K)
  148.     itsBuffer = NewHandle(recBufferSize);
  149.     FailNIL(itsBuffer);
  150.     itsBufferSize = recBufferSize;
  151.  
  152.  
  153.     /*
  154.     ** Don’t create TCP stream here. Therefore, if a session is never opened and the program
  155.     ** crashes (or ExitToShell is used by the programmer), MacTCP will remain stable.
  156.     */
  157.     
  158.     TCL_END_CONSTRUCTOR;
  159.  
  160. }
  161.  
  162.  
  163. /*______________________________________________________________________
  164. **
  165. ** destructor (private method)
  166. **
  167. **    DO NOT CALL THIS METHOD! Use Dispose() instead.
  168. **
  169. */
  170.  
  171. CTCPStream::~CTCPStream()
  172. {
  173.     TCL_START_DESTRUCTOR;
  174. }
  175.  
  176.  
  177. /*______________________________________________________________________
  178. **
  179. ** Dispose
  180. **
  181. **    Get rid of a TCP stream. Deallocates the receive buffer. If a session is still open,
  182. **    issues an Abort command, then postpones its disposal so completion routines still
  183. **    have a valid object to call.
  184. **
  185. */
  186.  
  187. void CTCPStream::Dispose()
  188.  
  189. {
  190.     TCPiopb theReleaseParam;
  191.  
  192.     // assume that the endpoint object is going away also
  193.  
  194.     itsEndpoint = NULL;
  195.  
  196.  
  197.     // we’re no longer in delayed disposal queue
  198.  
  199.     pendingDispose = FALSE;
  200.  
  201.  
  202.     // initiate a graceful close if necessary
  203.     
  204.     if (hasSessionOpen && !receivedClose) {
  205.         Close();
  206.         disposeOnTerminate = TRUE;
  207.         return;
  208.     }
  209.  
  210.  
  211.     // abort any connections that are yet to open
  212.     
  213.     if (pendingOpen) {
  214.         Abort();
  215.         disposeOnTerminate = TRUE;
  216.         return;
  217.     }
  218.  
  219.  
  220.     // if calls are still outstanding, wait a while
  221.     
  222.     if (!itsAsyncCalls->IsEmpty()) {
  223.         if ((macTCPStream) && pendingOpen) {                // get rid of the thing…
  224.             Abort();
  225.             PostponeDispose();
  226.             return;
  227.         }
  228.         if ((macTCPStream) && pendingOpen)
  229.             DoSyncCall(TCPRelease, &theReleaseParam);        // perhaps this will shake it loose
  230.         PostponeDispose();
  231.         return;
  232.     }
  233.  
  234.  
  235.     // if connection is still there, release stream to see if that shakes things loose
  236.     
  237.     if (macTCPStream && hasSessionOpen && !receivedTerminate) {
  238.         DoSyncCall(TCPRelease, &theReleaseParam);
  239.         macTCPStream = NULL;
  240.         PostponeDispose();
  241.         return;        
  242.     }
  243.  
  244.  
  245.     // make sure that terminate notice has been received
  246.     
  247.     if (hasSessionOpen && !receivedTerminate) {
  248.         PostponeDispose();
  249.         return;
  250.     }    
  251.  
  252.  
  253.     // release the stream from MacTCP
  254.     
  255.     if (macTCPStream)
  256.         DoSyncCall(TCPRelease, &theReleaseParam);
  257.     CTCPDriver::gTCPDriver->RemoveActiveStream(this);
  258.  
  259.  
  260.     // forget the async calls list
  261.     
  262.     ForgetObject(itsAsyncCalls);
  263.  
  264.  
  265.     // release the receive buffer
  266.     
  267.     ForgetHandle(itsBuffer);
  268.     macTCPStream = NULL;
  269.     itsBufferSize = 0L;
  270.  
  271.     delete this;
  272. }
  273.  
  274.  
  275. //    —— basic user TCP calls ——
  276.  
  277. /*______________________________________________________________________
  278. **
  279. ** OpenConnection
  280. **
  281. **    Opens a TCP session (either passive or active). Note that MacTCP seems to fail when
  282. **    non-zero values are used for theRemoteIP and theRemotePort.
  283. **
  284. **        passive (Boolean):        TRUE for passive open; FALSE for active open
  285. **        theRemoteIP (ip_addr):    IP address of remote host (0 for any host)
  286. **        theRemotePort (b_16):    TCP port of remote host (0 for any port)
  287. **        theLocalPort (b_16):        TCP port for local host (0 for any port)
  288. **
  289. */
  290.  
  291. void CTCPStream::OpenConnection(Boolean passive, ip_addr theRemoteIP,
  292.                             b_16 theRemotePort, b_16 theLocalPort)
  293.  
  294. {
  295.     TCPiopb    theCreateParam;
  296.     TCPiopb    theOpenParam;
  297.     OSErr    theResult;
  298.  
  299.     
  300.     // ensure that MacTCP is awake & ready for us
  301.     
  302.     if (!(CTCPDriver::gTCPDriver->CheckTCPDriver()))
  303.         FailOSErr(noTCPError);
  304.  
  305.  
  306.     // if the stream hasn’t been created yet, create one now
  307.     
  308.     if (!macTCPStream) {
  309.         MoveHHi(itsBuffer);                            // lock the receive buffer
  310.         HLock(itsBuffer);
  311.                 
  312.         theCreateParam.csParam.create.rcvBuff = *itsBuffer;
  313.         theCreateParam.csParam.create.rcvBuffLen = itsBufferSize;
  314.         #if USESROUTINEDESCRIPTORS
  315.             theCreateParam.csParam.create.notifyProc = (TCPNotifyProc) notifyProcUPP;
  316.         #else
  317.             theCreateParam.csParam.create.notifyProc = &NotifyProc;
  318.         #endif
  319.         theCreateParam.csParam.create.userDataPtr = (Ptr) this;
  320.         
  321.         macTCPStream = (Ptr) 1;                        // preempt the check for no stream present
  322.         theResult = DoSyncCall(TCPCreate, &theCreateParam);
  323.         
  324.         if (theResult == noErr) {                        // did we get a valid stream?
  325.             CTCPDriver::gTCPDriver->RegisterActiveStream(this);    //    yes, register in global stream list
  326.             macTCPStream = (Ptr) theCreateParam.tcpStream;
  327.         } else {
  328.             macTCPStream = NULL;
  329.             HandleOpenFailed(theResult);
  330.         }
  331.     }
  332.  
  333.  
  334.     // don’t allow open if already opening
  335.     
  336.     if (!(hasSessionOpen || pendingOpen)) {
  337.  
  338.         // fill in parms to open call
  339.         
  340.         theOpenParam.csParam.open.ulpTimeoutValue = itsULPtimeout;
  341.         theOpenParam.csParam.open.ulpTimeoutAction = itsULPaction;
  342.         theOpenParam.csParam.open.validityFlags = itsValidityFlag;
  343.         theOpenParam.csParam.open.commandTimeoutValue = itsCommandTimeoutValue;
  344.         theOpenParam.csParam.open.remoteHost = theRemoteIP;
  345.         theOpenParam.csParam.open.remotePort = theRemotePort;
  346.         theOpenParam.csParam.open.localPort = theLocalPort;
  347.         theOpenParam.csParam.open.tosFlags = itsTosFlags;
  348.         theOpenParam.csParam.open.precedence = itsPrecedence;
  349.         theOpenParam.csParam.open.dontFrag = itsDontFrag;
  350.         theOpenParam.csParam.open.timeToLive = itsTimeToLive;
  351.         theOpenParam.csParam.open.security = itsSecurity;
  352.         theOpenParam.csParam.open.optionCnt = itsOptionCnt;
  353.         BlockMove(&itsOptions, &theOpenParam.csParam.open.options, 40);
  354.         theOpenParam.csParam.open.userDataPtr = (Ptr) this;
  355.         pendingOpen = TRUE;
  356.         receivedClose = receivedTerminate = FALSE;
  357.     
  358.         // perform the call
  359.         
  360.         theResult = DoAsyncCall((passive ? TCPPassiveOpen : TCPActiveOpen), &theOpenParam);
  361.         if (theResult)
  362.             HandleOpenFailed(theResult);
  363.         
  364.         // pull IP address/port info from remote host
  365.         
  366.         itsRemoteIP = theOpenParam.csParam.open.remoteHost;
  367.         itsRemotePort = theOpenParam.csParam.open.remotePort;
  368.         itsLocalIP = theOpenParam.csParam.open.localHost;
  369.         itsLocalPort = theOpenParam.csParam.open.localPort;
  370.     }
  371.     else
  372.         HandleOpenFailed(connectionExists);
  373.     
  374. }
  375.  
  376.  
  377. /*______________________________________________________________________
  378. **
  379. ** Close
  380. **
  381. **    Signal that the connection should be closed. The stream’s owner should wait until it
  382. **    receives the tcpStreamClosed message before disposing of the stream object.
  383. **    This will allow time for the host to close gracefully.
  384. **
  385. */
  386.  
  387. void CTCPStream::Close()
  388.  
  389. {
  390.     TCPiopb    theCloseParam;
  391.     OSErr    theResult;
  392.  
  393.         if ((hasSessionOpen && (!pendingAbort) && (!pendingClose)) || remoteClose) {
  394.         pendingClose = TRUE;
  395.         pendingOpen = remoteClose = FALSE;
  396.         theCloseParam.csParam.close.ulpTimeoutValue = itsULPtimeout;
  397.         theCloseParam.csParam.close.ulpTimeoutAction = itsULPaction;
  398.         theCloseParam.csParam.close.validityFlags = itsValidityFlag;
  399.         theCloseParam.csParam.close.userDataPtr = (Ptr) this;
  400.         theResult = DoAsyncCall(TCPClose, &theCloseParam);
  401.         if ((theResult != noErr) && (theResult != connectionDoesntExist))
  402.             HandleTCPError(theResult, TCPClose);
  403.     }
  404. }
  405.  
  406.  
  407. /*______________________________________________________________________
  408. **
  409. ** Abort
  410. **
  411. **    Cancel the connection immediately. This call is performed synchronously. USE THIS CALL
  412. **    WITH CAUTION! MacTCP seems to get quite confused when a session is closed and
  413. **    data hasn’t all been received.
  414. **
  415. */
  416.  
  417. void CTCPStream::Abort()
  418.  
  419. {
  420.     TCPiopb    theAbortParam;
  421.     OSErr    theResult;
  422.  
  423.     if ((hasSessionOpen || pendingOpen) && (!pendingAbort)) {
  424.         pendingAbort = TRUE;
  425.         pendingOpen = pendingClose = remoteClose = FALSE;
  426.         theAbortParam.csParam.abort.userDataPtr = (Ptr) this;
  427.         theResult = DoSyncCall(TCPAbort, &theAbortParam);
  428.         if (theResult)
  429.             HandleTCPError(theResult, TCPAbort);
  430.         else
  431.             receivedClose = TRUE;
  432.     }
  433. }
  434.  
  435.  
  436. /*______________________________________________________________________
  437. **
  438. ** NoCopyRcv
  439. **
  440. **    Receive data without copying from TCP’s internal buffers. The completion routine in
  441. **    CTCPAsyncCall will take care of returning the RDS automatically. IF YOU ARE USING
  442. **    AUTO-RECEIVE, DO NOT CALL THIS METHOD!
  443. **
  444. **        itsRDS (rdsEntry*):        receive data structure (see MacTCP manual, p30)
  445. **        itsRDSSize (b_16):        number of entries in RDS (6 bytes each)
  446. **        itsTimeOut (b_16):        command timeout value in seconds (0 = infinite)
  447. **
  448. */
  449.  
  450. void CTCPStream::NoCopyRcv(rdsEntry* itsRDS, b_16 itsRDSSize, b_16 itsTimeOut)
  451.  
  452. {
  453.     TCPiopb    theRcvParam;
  454.     OSErr    theResult;
  455.  
  456.     theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
  457.     theRcvParam.csParam.receive.rdsPtr = (Ptr) itsRDS;
  458.     theRcvParam.csParam.receive.rdsLength = (unsigned short) Min(itsRDSSize, autoReceiveMax);
  459.     theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  460.     theRcvParam.csParam.open.options[36] = (Byte) itsAutoReceiveSize;
  461.  
  462.     theResult = DoAsyncCall(TCPNoCopyRcv, &theRcvParam);
  463.     if ((theResult != noErr) && (theResult != connectionClosing))
  464.         HandleTCPError(theResult, TCPNoCopyRcv);
  465.  
  466. }
  467.  
  468.  
  469. /*______________________________________________________________________
  470. **
  471. ** BfrReturn
  472. **
  473. **    Return a receive buffer to MacTCP used by the NoCopyRcv method. You should never need
  474. **    to call this method, since it is done automatically by the CTCPAsyncCall::Dispatch()
  475. **    method which handles all asynchronous completions.
  476. **
  477. **        itsRDS (Ptr):    the RDS structure to return
  478. **
  479. */
  480.  
  481. void CTCPStream::BfrReturn(Ptr itsRDS)
  482.  
  483. {
  484.     TCPiopb    theRcvParam;
  485.     OSErr    theResult;
  486.  
  487.  
  488.     // reject attempt to return buffers after the session is closed…
  489.     //    MacTCP has done it already (I think)
  490.  
  491.     if (hasSessionOpen) {
  492.         theRcvParam.csParam.receive.rdsPtr = itsRDS;
  493.         theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  494.     
  495.         DoSyncCall(TCPRcvBfrReturn, &theRcvParam);
  496.                                 // ignore errors — this is bad practice, but…
  497.     }
  498.  
  499. }
  500.  
  501.  
  502. /*______________________________________________________________________
  503. **
  504. ** Send
  505. **
  506. **    Send data on the TCP stream using the Write Data Structure (WDS) structure. The
  507. **    asynchronous completion routine can optionally dispose (DisposPtr) of the WDS and its
  508. **    associated data structures once the write operation is completed. If so, the buffers you
  509. **    provide must be expendable.
  510. **
  511. **        itsWDS (wdsEntry*):        write data structure (see MacTCP manual, p30)
  512. **        itsTimeOut (b_16):            command timeout value in seconds (0 = infinite)
  513. **        disposeWDS (Boolean):        dispose of WDS and data structures when completed
  514. **        notifyWhenDone (Boolean):    notify endpoint object when send operation completes
  515. **                                    (use HandleDataSent or HandleSendFailed)
  516. **
  517. */
  518.  
  519. void CTCPStream::Send(wdsEntry* itsWDS, b_16 itsTimeOut, Boolean disposeWDS,
  520.                         Boolean notifyWhenDone)
  521.  
  522. {
  523.     TCPiopb    theSendParam;
  524.     OSErr    theResult;
  525.  
  526.  
  527.     // ensure that at least one byte is being sent
  528.     
  529.     
  530.     if ((*itsWDS).length == 0)
  531.         return;
  532.     
  533.  
  534.     // fill in parms to send call
  535.     
  536.     theSendParam.csParam.send.ulpTimeoutValue = itsTimeOut;
  537.     theSendParam.csParam.send.ulpTimeoutAction = itsULPaction;
  538.     theSendParam.csParam.send.validityFlags = itsValidityFlag;
  539.     theSendParam.csParam.send.pushFlag = sendNextPush;
  540.     theSendParam.csParam.send.urgentFlag = sendNextUrgent;
  541.     theSendParam.csParam.send.wdsPtr = (Ptr) itsWDS;
  542.     theSendParam.csParam.send.userDataPtr = (Ptr) this;
  543.     
  544.     theSendParam.csParam.open.timeToLive = notifyWhenDone;
  545.     theSendParam.csParam.open.security = disposeWDS;
  546.                     // ^^^^ this isn’t pretty, but it’s efficient
  547.  
  548.     sendNextPush = FALSE;
  549.     sendNextUrgent = 0;
  550.  
  551.  
  552.     // perform the call
  553.     
  554.     theResult = DoAsyncCall(TCPSend, &theSendParam);
  555.     if (theResult)
  556.         HandleTCPError(theResult, TCPSend);
  557. }
  558.  
  559.  
  560. /*______________________________________________________________________
  561. **
  562. ** Receive
  563. **
  564. **    Receive data and copy to a user buffer.
  565. **
  566. **        theData (Ptr):        receive buffer
  567. **        theDataSize (b_16):    size of receive buffer in bytes
  568. **        itsTimeOut (b_16):    command timeout value in seconds (0 = infinite)
  569. **
  570. */
  571.  
  572. void CTCPStream::Receive(Ptr theData, b_16 itsDataSize, b_16 itsTimeOut)
  573.  
  574. {
  575.     TCPiopb    theRcvParam;
  576.     OSErr    theResult;
  577.  
  578.     theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
  579.     theRcvParam.csParam.receive.rcvBuff = theData;
  580.     theRcvParam.csParam.receive.rcvBuffLen = (unsigned short) itsDataSize;
  581.     theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
  582.  
  583.     theResult = DoAsyncCall(TCPRcv, &theRcvParam);
  584.     if (theResult)
  585.         HandleTCPError(theResult, TCPRcv);
  586. }
  587.  
  588.  
  589. /*______________________________________________________________________
  590. **
  591. ** Status
  592. **
  593. **    Returns a large pile of information about the TCP connection.
  594. **
  595. **        theStatusBlock (TCPStatusPB*):    user buffer for TCP status info
  596. **
  597. */
  598.  
  599. void CTCPStream::Status (TCPStatusPB* theStatusBlock)
  600.  
  601. {
  602.     TCPiopb    theStatusParam;
  603.     short    i;
  604.     Ptr        p;
  605.     OSErr    result;
  606.     
  607.     result = DoSyncCall(TCPStatus, &theStatusParam);
  608.     BlockMove(&theStatusParam.csParam, theStatusBlock, 66);
  609.     if ((result) || (theStatusParam.ioResult != noErr)) {
  610.         i = 0;
  611.         p = (Ptr) theStatusBlock;
  612.         while (i++<66)
  613.             *(p++) = 0;
  614.     }
  615. }
  616.  
  617.  
  618. /*______________________________________________________________________
  619. **
  620. ** ConnectionState
  621. **
  622. **    Returns the current status of the MacTCP stream. For definitions of the state codes,
  623. **    see TCP reference manual, p56.
  624. **
  625. **        return (b_16):    status of TCP stream
  626. **
  627. */
  628.  
  629. b_16 CTCPStream::ConnectionState()
  630.  
  631. {
  632.     TCPStatusPB theStatusBlock;
  633.     
  634.     Status(&theStatusBlock);
  635.     return theStatusBlock.connectionState;
  636. }
  637.  
  638.  
  639. //    —— specialized functions for sending data ——
  640.  
  641. /*______________________________________________________________________
  642. **
  643. ** SendBfrCpy
  644. **
  645. **    Send a range of bytes to the TCP stream. Prepares a WDS for the Send method and copies
  646. **    the bytes from the caller’s buffer to a data buffer which can persist beyond the procedure
  647. **    or object which generated the data.
  648. **
  649. **        theData (const void*):        the bytes to send
  650. **        theDataSize (unsigned short):    number of bytes to send
  651. **
  652. */
  653.  
  654. void CTCPStream::SendBfrCpy(const void* theData, unsigned short theDataSize)
  655.  
  656. {
  657.     wdsEntry*    itsWDS = NULL;
  658.     Ptr            itsNewBuffer = NULL;
  659.     
  660.  
  661.     // make sure there’s something to send
  662.  
  663.     if (theDataSize == 0)
  664.         return;
  665.     
  666.     TRY {
  667.     
  668.         // create the two buffers
  669.  
  670.         itsWDS = (wdsEntry*) NewPtr(8);            // WDS with room for one entry
  671.         itsNewBuffer = NewPtr(theDataSize);
  672.  
  673.  
  674.         // copy stuff to new buffer
  675.         
  676.         BlockMove(theData, itsNewBuffer, theDataSize);
  677.         (*itsWDS).length = theDataSize;
  678.         (*itsWDS).ptr = itsNewBuffer;
  679.         (*(++itsWDS)).length = 0;
  680.         --itsWDS;
  681.  
  682.  
  683.         // send data, using default timeout value
  684.         
  685.         Send(itsWDS, itsULPtimeout, TRUE, FALSE);
  686.                                             // TurboTCP 2.0 NOTE: completion of Send
  687.                                             //        is no longer called
  688.     }
  689.     
  690.     CATCH {
  691.         ForgetPtr(itsWDS);
  692.         ForgetPtr(itsNewBuffer);
  693.     }
  694.     ENDTRY;
  695.  
  696. }
  697.  
  698.  
  699. /*______________________________________________________________________
  700. **
  701. ** SendBfrNoCpy
  702. **
  703. **    Send a range of bytes to the TCP stream. Prepares a WDS for the Send method. Does not
  704. **    copy the bytes.
  705. **
  706. **        theData (const void*):        the bytes to send
  707. **        theDataSize (unsigned short):    number of bytes to send
  708. **        disposeWhenDone (Boolean):    set to TRUE to dispose of the buffer (DisposPtr)
  709. **                                    when completed (only if this pointer was
  710. **                                    allocated as a pointer, not a handle deref)
  711. **        notifyWhenDone (Boolean):    set to TRUE to notify the endpoint object
  712. **                                    when completed (via HandleDataSent or
  713. **                                    HandleSendFailed)
  714. **
  715. */
  716.  
  717. void CTCPStream::SendBfrNoCpy(const void* theData, unsigned short theDataSize,
  718.                             Boolean disposeWhenDone, Boolean notifyWhenDone)
  719.  
  720. {
  721.     wdsEntry *itsWDS = NULL;
  722.     
  723.  
  724.     // make sure there’s something to send
  725.  
  726.     if (theDataSize == 0)
  727.         return;
  728.     
  729.     TRY {
  730.  
  731.         // create the WDS buffer & fill it in
  732.  
  733.         itsWDS = (wdsEntry*) NewPtr(8);            // WDS with room for one entry
  734.         
  735.         (*itsWDS).length = theDataSize;
  736.         (*itsWDS).ptr = (Ptr) theData;
  737.         (*(++itsWDS)).length = 0;
  738.         --itsWDS;
  739.  
  740.  
  741.         // send data, using default timeout value
  742.         
  743.         Send(itsWDS, itsULPtimeout, disposeWhenDone, notifyWhenDone);
  744.     }
  745.     
  746.     CATCH {
  747.         ForgetPtr(itsWDS);
  748.     }
  749.     ENDTRY;
  750.  
  751. }
  752.  
  753.  
  754. //    —— notification routines ——
  755.  
  756. /*______________________________________________________________________
  757. **
  758. ** HandleDataSent (private method)
  759. **
  760. **    Respond to successful data sending. Notifies endpoint object and disposes of data if
  761. **    requested. Disposes of WDS structure.
  762. **
  763. **        WDSPtr (wdsEntry*):        the WDS structure
  764. **        disposeWhenDone (Boolean):    true to dispose of objects
  765. **        notifyWhenDone (Boolean):    true to notify endpoint object
  766. **
  767. */
  768.  
  769. void CTCPStream::HandleDataSent(wdsEntry* WDSPtr, Boolean disposeWhenDone,
  770.                             Boolean notifyWhenDone)
  771.  
  772. {
  773.     wdsEntry* theDisposePtr = WDSPtr;
  774.     while ((*theDisposePtr).length) {
  775.         if ((notifyWhenDone) && (itsEndpoint))            // 2.0b4
  776.             itsEndpoint->HandleDataSent((*theDisposePtr).ptr, (*theDisposePtr).length);
  777.         if (disposeWhenDone)
  778.             ForgetPtr((*theDisposePtr).ptr);
  779.         theDisposePtr++;
  780.     }
  781.     ForgetPtr(WDSPtr);
  782. }
  783.  
  784.  
  785. /*______________________________________________________________________
  786. **
  787. ** HandleSendFailed (private method)
  788. **
  789. **    Respond to failure to send data. Notifies endpoint object and disposes of data if requested.
  790. **    Disposes of WDS structure.
  791. **
  792. **        WDSPtr (wdsEntry*):        the WDS structure
  793. **        disposeWhenDone (Boolean):    true to dispose of objects
  794. **        notifyWhenDone (Boolean):    true to notify endpoint object
  795. **        theResultCode (OSErr):        the reason for failure
  796. **
  797. */
  798.  
  799. void CTCPStream::HandleSendFailed(wdsEntry* WDSPtr, Boolean disposeWhenDone,
  800.                             Boolean notifyWhenDone, OSErr theResultCode)
  801.  
  802. {
  803.     // send error notification (once only)
  804.  
  805.     if (itsEndpoint)                                // 2.0b4
  806.         itsEndpoint->HandleTCPError(theResultCode, TCPSend);
  807.     else                                        // 2.0b4
  808.         disposeWhenDone = true;                    // 2.0b4
  809.  
  810.  
  811.     // notify and dispose each data packet in WDS
  812.     
  813.     wdsEntry* theDisposePtr = WDSPtr;
  814.     while ((*theDisposePtr).length) {
  815.         if ((notifyWhenDone) && (itsEndpoint))        // 2.0b4
  816.             itsEndpoint->HandleSendFailed((*theDisposePtr).ptr, (*theDisposePtr).length, theResultCode);
  817.         if (disposeWhenDone)
  818.             ForgetPtr((*theDisposePtr).ptr);
  819.         theDisposePtr++;
  820.     }
  821.     ForgetPtr(WDSPtr);
  822.  
  823. }
  824.  
  825.  
  826. //    —— private methods for initiating TCP calls ——
  827.  
  828. /*______________________________________________________________________
  829. **
  830. ** DoAsyncCall (private method)
  831. **
  832. **    Creates a CTCPAsyncCall object and uses it to execute a TCP Device Manager call
  833. **    asynchronously. Does not wait for completion. Fails if not enough memory to
  834. **    create call object.
  835. **
  836. **        theCsCode (b_16):                TCP operation code
  837. **        theParamBlockPtr (TCPiopb*):        parameter block for TCP call
  838. **
  839. **        return (OSErr):                    result code (+1 is not returned)
  840. **
  841. */
  842.  
  843. OSErr CTCPStream::DoAsyncCall(b_16 theCsCode, TCPiopb* theParamBlockPtr)
  844.  
  845. {
  846.     OSErr            theResult;
  847.     CTCPAsyncCall*    theCall;
  848.  
  849.  
  850.     // make sure a stream was opened
  851.     
  852.     if (macTCPStream)
  853.         (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
  854.     else
  855.         return invalidStreamPtr;
  856.  
  857.  
  858.     // create call object
  859.     
  860.     theCall = new CTCPAsyncCall(*this);
  861.     theResult = theCall->DoAsyncCall(theCsCode, theParamBlockPtr);
  862.     if (theResult == inProgress)
  863.         itsAsyncCalls->Add(theCall);
  864.     return (theResult == inProgress) ? noErr : theResult;
  865. }
  866.  
  867.  
  868. /*______________________________________________________________________
  869. **
  870. ** DoSyncCall (private method)
  871. **
  872. **    Fills in the standard parameters for a TCP Device Manager call and executes the call.
  873. **    Waits for completion of the call.
  874. **
  875. **        theCsCode (b_16):                TCP operation code
  876. **        theParamBlockPtr (TCPiopb*):        parameter block for TCP call
  877. **
  878. **        return (OSErr):                    result code (+1 is not returned)
  879. **
  880. */
  881.  
  882. OSErr CTCPStream::DoSyncCall(b_16 theCsCode, TCPiopb* theParamBlockPtr)
  883.  
  884. {
  885.  
  886.     // make sure a stream was opened
  887.     
  888.     if (macTCPStream)
  889.         (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
  890.     else
  891.         return invalidStreamPtr;
  892.  
  893.  
  894.     // perform the call
  895.     
  896.     (*theParamBlockPtr).ioCompletion = (TCPIOCompletionProc) NULL;
  897.     (*theParamBlockPtr).ioCRefNum = CTCPDriver::gTCPDriver->GetTCPRefNum();
  898.     (*theParamBlockPtr).csCode = theCsCode;
  899.     PBControlSync((ParmBlkPtr) theParamBlockPtr);
  900.     return (*theParamBlockPtr).ioResult;
  901. }
  902.  
  903.  
  904. /*______________________________________________________________________
  905. **
  906. ** IssueAutoReceive (private method)
  907. **
  908. **    Issue a NoCopyRcv command to grab the next batch of data. Does nothing if auto-receive
  909. **    is not enabled.
  910. **
  911. */
  912.  
  913. void CTCPStream::IssueAutoReceive()
  914.  
  915. {
  916.     // create a new RDS with room for # of entries requested & send it to TCP
  917.     
  918.     Ptr    newRDS;
  919.     if (hasSessionOpen && itsAutoReceiveSize) {
  920.         FailNIL(newRDS = NewPtr((itsAutoReceiveSize*6)+2));
  921.         NoCopyRcv((rdsEntry*) newRDS, itsAutoReceiveSize, 0);
  922.     }
  923. }
  924.  
  925.  
  926. /*______________________________________________________________________
  927. **
  928. ** ProcessNotify
  929. **
  930. **    Respond to notifications received by the ASR during interrupt time. This routine is free
  931. **    of interrupt-level constraints.
  932. **
  933. */
  934.  
  935. void CTCPStream::ProcessNotify()
  936.  
  937. {
  938.     // no longer in notify queue
  939.     
  940.     pendingNotify = FALSE;
  941.     
  942.     
  943.     // if terminated, inform the stream owner & dispose (if appropriate)
  944.     //    clear all other notifications (they no longer apply)
  945.  
  946.     if (notifTerminate) {
  947.         HandleTerminated(notifTermReason);
  948.         hasSessionOpen = pendingOpen = pendingClose = 
  949.             remoteClose = pendingAbort = notifClosing = notifTimeout =
  950.             notifDataArrived = notifUrgent = FALSE;
  951.         receivedClose = receivedTerminate = TRUE;
  952.         if (disposeOnTerminate)
  953.             PostponeDispose();
  954.     }
  955.  
  956.  
  957.     // if closed, inform stream owner, wait for termination before disposing (if appropriate)
  958.  
  959.     if (notifClosing) {
  960.         HandleClosing(remoteClose);
  961.         receivedClose = TRUE;
  962.         notifClosing = FALSE;
  963.         if (disposeOnTerminate)
  964.             PostponeDispose();
  965.     }
  966.  
  967.  
  968.     // if urgent notification, flag beginning of urgent data
  969.  
  970.     if (notifUrgent) {
  971.         if (!rcvUrgent) {
  972.             RcvUrgentBegin();
  973.             HandleUrgentBegin();
  974.         }
  975.         notifUrgent = FALSE;
  976.     }
  977.  
  978.  
  979.     // tell stream owner about other notifications, TurboTCP doesn’t respond to these
  980.  
  981.     if (notifTimeout) {
  982.         HandleTimeout();
  983.         notifTimeout = FALSE;
  984.     }
  985.     
  986.     if (notifDataArrived) {
  987.         HandleUnexpectedData();
  988.         notifDataArrived = FALSE;
  989.     }
  990.     
  991.     if (notifICMP) {
  992.         HandleICMP(¬ifICMPreport);
  993.         notifICMP = FALSE;
  994.     }
  995.  
  996. }
  997.  
  998.  
  999. /*______________________________________________________________________
  1000. **
  1001. ** ProcessAsyncCompletion
  1002. **
  1003. **    Respond to the completion of an asynchronous call. Removes the async call object from
  1004. **    the list of outstanding calls.
  1005. **
  1006. **        theCall (CTCPAsyncCall*):    the call which was completed
  1007. **
  1008. */
  1009.  
  1010. void CTCPStream::ProcessAsyncCompletion(CTCPAsyncCall* theCall)
  1011.  
  1012. {
  1013.     itsAsyncCalls->Remove(theCall);
  1014. }
  1015.  
  1016.  
  1017. /***********************************************************************
  1018. ************************************************************************
  1019. **
  1020. **    INTERRUPT-LEVEL routines follow. These routines cannot make memory allocations, cannot make
  1021. **    synchronous TCP calls, and cannot use the Think C profiler.
  1022. **
  1023. */
  1024.  
  1025. #ifndef __MWERKS__
  1026. //#pragma options(!profile)
  1027. #else
  1028. #pragma profile off
  1029. #endif
  1030.  
  1031.  
  1032. /*______________________________________________________________________
  1033. **
  1034. ** PostponeDispose
  1035. **
  1036. **    Add this stream object to the delayed disposal queue. This routine may be called during
  1037. **    an interrupt.
  1038. **
  1039. */
  1040.  
  1041. void CTCPStream::PostponeDispose()
  1042.  
  1043. {
  1044.     if (pendingDispose)                            // ignore this if we’re already in disposal queue
  1045.         return;
  1046.     pendingDispose = TRUE;
  1047.     disposeOnTerminate = TRUE;
  1048.     Enqueue((QElemPtr) &qDisposeEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
  1049. }
  1050.  
  1051.  
  1052. /*______________________________________________________________________
  1053. **
  1054. ** PostponeNotify (protected method)
  1055. **
  1056. **    The asynchronous notification routine (ASR). Receives notification of events for all TCP
  1057. **    streams. Dispatches the notification to the proper method of the CTCPStream object. This
  1058. **    routine is installed for all TCP streams created by the CTCPStream object.
  1059. **
  1060. **    This method merely logs the kind of notification and adds this stream to the list of streams
  1061. **    to be processed next time through the event loop.
  1062. **
  1063. **        eventCode (b_16):            event code (see MacTCP manual, p37)
  1064. **        terminReason (b_16):        reason for termination (if applicable)
  1065. **        icmpMsg (structICMPReport*):    ICMP report (if applicable)
  1066. **
  1067. */
  1068.  
  1069. void CTCPStream::PostponeNotify(b_16 eventCode, b_16 terminReason, struct ICMPReport* icmpMsg)
  1070.  
  1071. {
  1072.  
  1073.     // reject notifications for unexpected data if auto-receiving
  1074.     
  1075.     if ((itsAutoReceiveSize > 0) && (eventCode == TCPDataArrival))
  1076.         return;
  1077.  
  1078.  
  1079.     // install this stream in asynchronous events queue
  1080.     //    CTCPDriver::ProcessNetEvents will pick it up later
  1081.  
  1082.     if (!pendingNotify) {
  1083.         Enqueue((QElemPtr) &qNotifyEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
  1084.         pendingNotify = TRUE;
  1085.     }
  1086.  
  1087.  
  1088.     // record the event type
  1089.  
  1090.     switch (eventCode) {
  1091.         case TCPClosing:
  1092.             remoteClose = remoteClose || !pendingClose;
  1093.             notifClosing = pendingClose = TRUE;
  1094.             break;
  1095.  
  1096.         case TCPULPTimeout:
  1097.             notifTimeout = TRUE;
  1098.             break;
  1099.  
  1100.         case TCPTerminate:
  1101.             notifTerminate = TRUE;
  1102.             notifTermReason = terminReason;
  1103.             break;
  1104.  
  1105.         case TCPDataArrival:
  1106.             notifDataArrived = TRUE;
  1107.             break;
  1108.  
  1109.         case TCPUrgent:
  1110.             notifUrgent = TRUE;
  1111.             break;
  1112.  
  1113.         case TCPICMPReceived:
  1114.             notifICMP = TRUE;
  1115.             BlockMove(icmpMsg, ¬ifICMPreport, 24);
  1116.             break;
  1117.     }
  1118.  
  1119. }
  1120.  
  1121.  
  1122. /*______________________________________________________________________
  1123. **
  1124. ** NotifyProc (private static method)
  1125. **
  1126. **    The asynchronous notification routine (ASR). This static method just decodes the
  1127. **    userDataPtr and calls PostponeNotify for the object named. This routine is the standard
  1128. **    ASR for all TCP streams created by the CTCPStream object.
  1129. **
  1130. **        tcpStream (StreamPtr):    the TCP stream in question
  1131. **        eventCode (short):        TCP event code (see MacTCP Dev guide, p37)
  1132. **        userDataPtr (Ptr):        user data pointer (in this case, the CTCPStream)
  1133. **        terminReason (short):    reason for termination (see MacTCP Dev guide, p37)
  1134. **        icmpMsg (…):            ICMP report (if applicable)
  1135. **
  1136. */
  1137.  
  1138. pascal void CTCPStream::NotifyProc(StreamPtr tcpStream, unsigned short eventCode,
  1139.                                 Ptr userDataPtr, unsigned short terminReason,
  1140.                                 struct ICMPReport* icmpMsg)
  1141.  
  1142. {
  1143.     CTCPStream* theTCPStream = (CTCPStream*) userDataPtr;
  1144.     theTCPStream->PostponeNotify(eventCode, terminReason, icmpMsg);
  1145. }
  1146.