home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Libraries / TurboTCP 2.0.1 / TurboTCP source / CTCPStream.cp < prev    next >
Encoding:
Text File  |  1994-11-22  |  29.2 KB  |  1,152 lines  |  [TEXT/MPCC]

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