home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Portable Patmos 1.1 / patmos-src / src / sock_tcp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-19  |  21.0 KB  |  961 lines  |  [TEXT/R*ch]

  1. /*
  2.  * BSD-style socket emulation library for the Mac
  3.  * Original author: Tom Milligan
  4.  * Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
  5.  *
  6.  * This source file is placed in the public domian.
  7.  * Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
  8.  *
  9.  *      National Center for Supercomputing Applications
  10.  *      152 Computing Applications Building
  11.  *      605 E. Springfield Ave.
  12.  *      Champaign, IL  61820
  13.  */
  14.  
  15. #ifdef USEDUMP
  16. # pragma load "Socket.dump"
  17.  
  18. #else
  19. # include <Events.h>
  20. # include <Memory.h>
  21. # include <Types.h>
  22. # include <OSUtils.h> /* for SysBeep */
  23. # include <Events.h> /* for TickCount */
  24. # include <Stdio.h>
  25.  
  26.  
  27. # include <s_types.h>
  28. # include <neti_in.h>
  29. # include <neterrno.h>
  30. # include <s_socket.h>
  31. # include <s_time.h>
  32. # include <s_uio.h>
  33.  
  34. # include "sock_str.h"
  35. # include "sock_int.h"
  36.  
  37. #endif
  38.  
  39. #ifndef THINK_C
  40. # include "sock_int.h"
  41. #endif
  42.  
  43. extern SocketPtr sockets;
  44. extern SpinFn spinroutine;    /* The spin routine. */ 
  45.  
  46. /*
  47.  * sock_tcp_new_stream
  48.  *
  49.  * Create a new tcp stream.
  50.  */
  51. int sock_tcp_new_stream(
  52.     SocketPtr sp)
  53. {
  54.     OSErr        io;
  55.     TCPiopb        pb;
  56.     StreamHashEntPtr shep;
  57.  
  58. #if SOCK_TCP_DEBUG >= 2
  59.     sock_print("sock_tcp_new_stream", sp);
  60. #endif
  61.     
  62.     io = xTCPCreate(STREAM_BUFFER_SIZE, sock_tcp_notify, &pb);
  63.     switch(io)
  64.     {
  65.         case noErr:                 break;
  66.         case streamAlreadyOpen:     return(sock_err(io));
  67.         case invalidLength:         return(sock_err(ENOBUFS));
  68.         case invalidBufPtr:         return(sock_err(ENOBUFS));
  69.         case insufficientResources: return(sock_err(EMFILE));
  70.         default: /* error from PBOpen */ return(sock_err(ENETDOWN));
  71.     }
  72.     sp->sstate = SOCK_STATE_UNCONNECTED;
  73.     sp->peer.sin_family = AF_INET;
  74.     sp->peer.sin_addr.s_addr = 0;
  75.     sp->peer.sin_port = 0;
  76.     bzero(&sp->peer.sin_zero[0], 8);
  77.     sp->dataavail = 0;
  78.     sp->asyncerr = 0;
  79.     sp->stream = pb.tcpStream;
  80.         
  81.     if ((shep = sock_new_shep(sp->stream))!=NULL)
  82.         {
  83.         shep -> stream = sp->stream;
  84.         shep -> socket = sp;
  85.         return(0);
  86.         }
  87.     else
  88.         return -1;
  89. }
  90.  
  91.  
  92. /*
  93.  *    sock_tcp_connect - initiate a connection on a TCP socket
  94.  *
  95.  */
  96. int sock_tcp_connect(
  97.     SocketPtr sp,
  98.     struct sockaddr_in *addr)
  99. {
  100.     OSErr io;
  101.     TCPiopb    *pb;
  102.     void sock_tcp_connect_done();
  103.     
  104. #if SOCK_TCP_DEBUG >= 2
  105.     sock_print("sock_tcp_connect",sp);
  106. #endif
  107.     
  108.     /* Make sure this socket can connect. */
  109.     if (sp->sstate == SOCK_STATE_CONNECTING)
  110.         return(sock_err(EALREADY));
  111.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  112.         return(sock_err(EISCONN));
  113.         
  114.     sp->sstate = SOCK_STATE_CONNECTING;
  115.     
  116.     if (!(pb=sock_fetch_pb(sp)))
  117.         return sock_err(ENOMEM);
  118.     
  119.     io = xTCPActiveOpen(pb, sp->sa.sin_port,addr->sin_addr.s_addr, addr->sin_port, 
  120.             sock_tcp_connect_done);
  121.             
  122.     if (io != noErr)
  123.     {
  124.         sp->sstate = SOCK_STATE_UNCONNECTED;
  125.         return(sock_err(io));
  126.     }
  127.     
  128.     if (sp->nonblocking)        
  129.         return(sock_err(EINPROGRESS));
  130.     
  131.     /* sync connect - spin till TCPActiveOpen completes */
  132.  
  133. #if SOCK_TCP_DEBUG >= 5
  134.     dprintf("spinning in connect\n");
  135. #endif
  136.  
  137.     SPIN (pb->ioResult==inProgress,SP_MISC,0L)
  138.  
  139. #if SOCK_TCP_DEBUG >= 5
  140.     dprintf("done spinning\n");
  141. #endif
  142.     
  143.     if ( pb->ioResult != noErr )
  144.         return sock_err(pb->ioResult);
  145.     else
  146.         return 0;
  147. }
  148.  
  149. /*
  150.  * sock_tcp_listen() - put s into the listen state.
  151.  */
  152. int sock_tcp_listen(
  153.     SocketPtr sp)
  154. {
  155.     OSErr        io;
  156.     TCPiopb        *pb;
  157.     void sock_tcp_listen_done(TCPiopb *pb);
  158.  
  159. #if SOCK_TCP_DEBUG >= 2
  160.     sock_print("sock_tcp_listen",sp);
  161. #endif
  162.  
  163.     if (!(pb = sock_fetch_pb(sp)))
  164.         return (sock_err(ENOMEM));
  165.     
  166.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  167.         return(sock_err(EISCONN));
  168.     
  169.     sp->sstate = SOCK_STATE_LISTENING;
  170.     
  171.     io = xTCPPassiveOpen(pb, sp->sa.sin_port, sock_tcp_listen_done);
  172.     if (io != noErr) 
  173.     {
  174.         sp->sstate = SOCK_STATE_UNCONNECTED;
  175.         return(sock_err(io));
  176.     }
  177. #if SOCK_TCP_DEBUG >= 5
  178.     dprintf("sock_tcp_listen: about to spin for port number - ticks %d\n",
  179.             TickCount());
  180. #endif
  181.     while (pb->csParam.open.localPort == 0)
  182.     {
  183. #if SOCK_TCP_DEBUG >= 5
  184.         tcpCheckNotify();
  185. #endif
  186.         SPIN(false,SP_MISC,0L)
  187.     }
  188. #if SOCK_TCP_DEBUG >= 5
  189.     dprintf("sock_tcp_listen: port number is %d ticks %d\n",
  190.     pb->csParam.open.localPort,TickCount());
  191. #endif
  192.     sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  193.     sp->sa.sin_port = pb->csParam.open.localPort;
  194.     return(0);
  195. }
  196.  
  197. /*
  198.  *    sock_tcp_accept()
  199.  */
  200. int sock_tcp_accept(
  201.     SocketPtr sp,
  202.     struct sockaddr_in *from,
  203.     Int4 *fromlen)
  204. {
  205.     int         s1;
  206.     TCPiopb        *pb;
  207.     StreamHashEntPtr shep;
  208.  
  209. #if SOCK_TCP_DEBUG >= 2
  210.     sock_print("sock_tcp_accept",sp);
  211. #endif
  212.  
  213.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  214.     {
  215.         if (sp->asyncerr != 0)
  216.         {
  217.             (void) sock_err(sp->asyncerr);
  218.             sp->asyncerr = 0;
  219.             return(-1);
  220.         }
  221.         else
  222.             return(sock_err(ENOTCONN));
  223.     }
  224.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  225.         return(sock_err(ENOTCONN));
  226.  
  227.     if (sp->sstate == SOCK_STATE_LISTENING) 
  228.     {    
  229.         if (sp->nonblocking) 
  230.             return(sock_err(EWOULDBLOCK));
  231.  
  232.         /*    Spin till sock_tcp_listen_done runs. */
  233. #if SOCK_TCP_DEBUG >= 5
  234.         dprintf("--- blocking...\n");
  235. #endif
  236.         
  237.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  238.  
  239. #if SOCK_TCP_DEBUG >= 5
  240.         dprintf("--- done blocking...\n");
  241. #endif
  242.         
  243.         /* got notification - was it success? */
  244.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  245.         {
  246. #if    SOCK_TCP_DEBUG >=3
  247.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  248. #endif
  249.             (void) sock_err(sp->asyncerr);
  250.             sp->asyncerr = 0;
  251.             return(-1);
  252.         }
  253.     }
  254. #if SOCK_TCP_DEBUG >= 3
  255.     dprintf("sock_tcp_accept: Have connection, peer is %08x/%d, duplicating socket.\n",
  256.             sp->peer.sin_addr,sp->peer.sin_port);
  257. #endif
  258.     /*
  259.      * Have connection.  Duplicate this socket.  The client gets the connection
  260.      * on the new socket and I create a new stream on the old socket and put it 
  261.      * in listen state. 
  262.      */
  263.     sp->sstate = SOCK_STATE_CONNECTED;
  264.  
  265.     s1 = sock_free_fd(0);
  266.     if (s1 < 0) 
  267.     {
  268.         /*    No descriptors left.  Abort the incoming connection. */
  269. #if SOCK_TCP_DEBUG >= 2
  270.         dprintf("sock_tcp_accept: No descriptors left.\n");
  271. #endif
  272.         if (!(pb = sock_fetch_pb (sp)))
  273.             return sock_err(ENOMEM);
  274.             
  275.         (void) xTCPAbort(pb);
  276.         sp->sstate = SOCK_STATE_UNCONNECTED;
  277.  
  278.         /* try and put the socket back in listen mode */
  279.         if (sock_tcp_listen(sp) < 0) 
  280.         {
  281. #if SOCK_TCP_DEBUG >= 1
  282.             dprintf("sock_tcp_accept: sock_tcp_listen fails\n");
  283. #endif
  284.             sp->sstate = SOCK_STATE_UNCONNECTED;
  285.             return(-1);        /* errno already set */
  286.         }
  287.         return(sock_err(EMFILE));
  288.     }
  289.     
  290.     /* copy the incoming connection to the new socket */
  291.     sock_dup_fd(sp->fd,s1);
  292. #if SOCK_TCP_DEBUG >= 3
  293.     dprintf("sock_tcp_accept: new socket is %d\n",s1);
  294.     sock_dump();
  295. #endif
  296.  
  297.     /* quitely adjust the StreamHash table */
  298.     if ((shep = sock_find_shep(sp->stream))!=NULL)
  299.         {
  300.         shep->socket = sockets+s1;        /* point to new socket */
  301.         }
  302.  
  303.     /* Create a new MacTCP stream on the old socket and put it into */
  304.     /* listen state to accept more connections. */
  305.     if (sock_tcp_new_stream(sp) < 0 || sock_tcp_listen(sp) < 0) 
  306.     {
  307. #if SOCK_TCP_DEBUG >= 2
  308.         dprintf("accept: failed to restart old socket\n");
  309. #endif
  310.         /* nothing to listen on */
  311.         sp->sstate = SOCK_STATE_UNCONNECTED;
  312.         
  313.         /* kill the incoming connection */
  314.         if (!(pb=sock_fetch_pb(sockets+s1)))
  315.             return sock_err(ENOMEM);
  316.             
  317.         xTCPRelease(pb);
  318.         sock_clear_fd(s1);
  319.         
  320.         return(-1); /* errno set */
  321.     }
  322. #if SOCK_TCP_DEBUG >= 3
  323.     dprintf("sock_tcp_accept: got new stream\n");
  324.     sock_dump();
  325. #endif
  326.  
  327.     /* return address of partner */
  328.     sock_copy_addr(&sockets[s1].peer, from, fromlen);
  329.  
  330.     return(s1);
  331. }
  332.  
  333. /*
  334.  *    sock_tcp_accept_once()
  335.  */
  336. int sock_tcp_accept_once(
  337.     SocketPtr sp,
  338.     struct sockaddr_in *from,
  339.     Int4 *fromlen)
  340. {
  341.  
  342. #if SOCK_TCP_DEBUG >= 2
  343.     sock_print("sock_tcp_accept_once",sp);
  344. #endif
  345.  
  346.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  347.     {
  348.         if (sp->asyncerr != 0)
  349.         {
  350.             (void) sock_err(sp->asyncerr);
  351.             sp->asyncerr = 0;
  352.             return(-1);
  353.         }
  354.         else
  355.             return(sock_err(ENOTCONN));
  356.     }
  357.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  358.         return(sock_err(ENOTCONN));
  359.  
  360.     if (sp->sstate == SOCK_STATE_LISTENING) 
  361.     {    
  362.         if (sp->nonblocking) 
  363.             return(sock_err(EWOULDBLOCK));
  364.  
  365.         /*    Spin till sock_tcp_listen_done runs. */
  366. #if SOCK_TCP_DEBUG >= 5
  367.         dprintf("--- blocking...\n");
  368. #endif
  369.         
  370.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  371.         
  372. #if SOCK_TCP_DEBUG >= 5
  373.         dprintf("--- done blocking...\n");
  374. #endif
  375.         
  376.         /* got notification - was it success? */
  377.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  378.         {
  379. #if    SOCK_TCP_DEBUG >=3
  380.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  381. #endif
  382.             (void) sock_err(sp->asyncerr);
  383.             sp->asyncerr = 0;
  384.             return(-1);
  385.         }
  386.     }
  387. #if SOCK_TCP_DEBUG >= 3
  388.     dprintf("sock_tcp_accept_once: Have connection, peer is %08x/%d.\n",
  389.             sp->peer.sin_addr,sp->peer.sin_port);
  390. #endif
  391.     /*
  392.      * Have connection.
  393.      */
  394.     sp->sstate = SOCK_STATE_CONNECTED;
  395.     
  396.     /* return address of partner */
  397.     sock_copy_addr(&(sp->peer), from, fromlen);
  398.  
  399.     return(0);
  400. }
  401.  
  402. /*
  403.  * sock_tcp_recv()
  404.  *
  405.  * returns bytes received or -1 and errno
  406.  */
  407. int sock_tcp_recv(
  408.     SocketPtr sp,
  409.     char *buffer,
  410.     int buflen,
  411.     int flags)
  412. {
  413. #pragma unused(flags)
  414.     void    sock_tcp_recv_done();
  415.     TCPiopb    *pb;
  416.     int iter; /* iteration */
  417.     
  418. #if SOCK_TCP_DEBUG >= 2
  419.     sock_print("sock_tcp_recv",sp);
  420. #endif
  421.  
  422.     /* socket hasn't finished connecting yet */
  423.     if (sp->sstate == SOCK_STATE_CONNECTING)
  424.     {
  425. #if SOCK_TCP_DEBUG >= 5
  426.         dprintf("sock_tcp_recv: connection still in progress\n");
  427. #endif
  428.         if (sp->nonblocking)
  429.             return(sock_err(EWOULDBLOCK));
  430.             
  431.         /* async connect and sync recv? */
  432. #if SOCK_TCP_DEBUG >= 5
  433.         dprintf("sock_tcp_recv: spinning on connect\n");
  434. #endif
  435.  
  436.         SPIN(sp->sstate == SOCK_STATE_CONNECTING,SP_MISC,0L)
  437.  
  438. #if SOCK_TCP_DEBUG >= 5
  439.         dprintf("sock_tcp_recv: done spinning\n");
  440. #endif
  441.     }
  442.         
  443.     /* socket is not connected */
  444.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  445.     {
  446.         /* see if the connect died (pretty poor test) */
  447.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  448.         {
  449.             (void) sock_err(sp->asyncerr);
  450.             sp->asyncerr = 0;
  451.             return(-1);
  452.         }
  453.  
  454.         /* I guess he just forgot */
  455.         return(sock_err(ENOTCONN));        
  456.     }
  457.             
  458.     if (sp->dataavail == 0)
  459.         sp->dataavail = xTCPBytesUnread(sp);        /* sync. call */
  460. #if SOCK_TCP_DEBUG >= 3
  461.     dprintf("sock_tcp_recv: %d bytes available\n", sp->dataavail);
  462. #endif
  463.     if (sp->nonblocking && sp->dataavail == 0) 
  464.             return(sock_err(EWOULDBLOCK));
  465.  
  466.     sp->torecv              = 1; /* # of bytes to try to receive */
  467.     sp->recvBuf             = buffer;
  468.     sp->recvd               = 0; /* count of bytes received */    
  469.     pb = sock_fetch_pb(sp);
  470.     
  471.     /* make 2 iterations; on 1st try, read 1 byte; on 2nd, read
  472.        all outstanding available bytes, up to buflen ... this mechanism seems to
  473.        be necessary for decent performance, because TCPRcv only completes when one
  474.        of the following takes place:
  475.        * enough data has arrived to fill the receive buffer
  476.        * pushed data arrives
  477.        * urgent data is outstanding
  478.        * some reasonable period passes after the arrival of nonpushed, nonurgent data
  479.        * the amount of data received is greater than or equal to 25 percent of the total
  480.          receive buffering for this stream
  481.        * the command time-out expires
  482.        
  483.        In the case when a caller has requested N bytes, and the data is "normal" TCP
  484.        data, the "reasonable" period must expire before this function will return. This
  485.        "reasonable" period appears to be about one second (MacTCP version 1.1), and is
  486.        not configurable. The hope in the algorithm implemented here is that a reasonable
  487.        amount of data will arrive along with the first byte, and that the caller is
  488.        capable of issuing another read() to obtain more data. The one-second "reasonable"
  489.        delay is thus eliminated.
  490.        
  491.        J. Epstein, NCBI, 06/24/92
  492.     */
  493.     
  494.     for (iter = 0; iter < 2; iter++) {
  495.  
  496.         sp->asyncerr            = inProgress;
  497.     
  498.         xTCPRcv(pb, sp->recvBuf, min (sp->torecv,TCP_MAX_MSG),0,
  499.             (TCPIOCompletionProc)sock_tcp_recv_done);
  500.     
  501.         SPIN(sp->torecv&&(pb->ioResult==noErr||pb->ioResult==inProgress),
  502.             SP_TCP_READ,sp->torecv)
  503.         
  504.         if ( pb->ioResult == commandTimeout )
  505.             pb->ioResult = noErr;
  506.  
  507.         switch(pb->ioResult)
  508.         {
  509.             case noErr:
  510. #if SOCK_TCP_DEBUG >= 3
  511.                 dprintf("sock_tcp_recv: got %d bytes\n", sp->recvd);
  512. #endif
  513.                 sp->dataavail = xTCPBytesUnread(sp);
  514.                 sp->asyncerr = noErr;
  515.                 if (sp->dataavail <= 0 || buflen <= 1 || iter == 1)
  516.                     return(sp->recvd);
  517.                 /* loop back and obtain the remaining outstanding data */
  518.                 sp->torecv = min(sp->dataavail, buflen - 1);
  519.                 sp->recvBuf = &buffer[1];
  520.                 break;
  521.                 
  522.             case connectionClosing:
  523. #if SOCK_TCP_DEBUG >= 2
  524.                 dprintf("sock_tcp_recv: connection closed\n");
  525. #endif
  526.                 return (sp->recvd);
  527.                 break;
  528.  
  529.  
  530.             case connectionTerminated:
  531.                 /* The connection is aborted. */
  532.                 sp->sstate = SOCK_STATE_UNCONNECTED;
  533. #if SOCK_TCP_DEBUG >= 1
  534.                 dprintf("sock_tcp_recv: connection gone!\n");
  535. #endif
  536.                 return(sock_err(ENOTCONN));
  537.  
  538.             case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  539.             case connectionDoesntExist:
  540.             case invalidStreamPtr:
  541.             case invalidLength:
  542.             case invalidBufPtr:
  543.             default:
  544.                 return(sock_err(pb->ioResult));
  545.         }
  546.     }
  547. }
  548.  
  549. /*
  550.  *    sock_tcp_can_read() - returns non-zero if data or a connection is available
  551.  *  must also return one if the connection is down to force an exit from the
  552.  *  select routine (select is the only thing that uses this).
  553.  */
  554. int sock_tcp_can_read(SocketPtr sp)
  555.     {
  556.     TCPiopb        *pb;
  557.     
  558.     if (sp->sstate == SOCK_STATE_LIS_CON) 
  559.         return(1);
  560.  
  561.     else if (sp->sstate == SOCK_STATE_CONNECTED) 
  562.         {
  563.         if (!(pb=sock_fetch_pb(sp)))
  564.             return sock_err (ENOMEM);
  565.         
  566.         sp->dataavail = xTCPBytesUnread(sp);
  567.         if (sp->dataavail > 0) 
  568.             return(1);
  569.         }
  570.     else if ( ( sp->sstate == SOCK_STATE_UNCONNECTED ) || 
  571.             ( sp->sstate == SOCK_STATE_CLOSING ) )
  572.         return 1;
  573.  
  574.     return(0);
  575.     }
  576.  
  577. /* avoid using standard library here because size of an int may vary externally */
  578. void *sock_memset(void *s, int c, size_t n)
  579. {
  580.     char *t = s;
  581.     
  582.     for (; n > 0; n--, t++)
  583.         *t = c;
  584. }
  585.     
  586.  
  587. /*
  588.  *    sock_tcp_send() - send data
  589.  *
  590.  *    returns bytes sent or -1 and errno
  591.  */
  592. int sock_tcp_send(
  593.     SocketPtr sp,
  594.     char *buffer,
  595.     int count,
  596.     int flags)
  597. {
  598.     int        bytes,towrite;
  599.     miniwds    *thiswds;
  600.     short    wdsnum;
  601.     TCPiopb    *pb;
  602.     miniwds    wdsarray[TCP_MAX_WDS];
  603.     void    sock_tcp_send_done();
  604.  
  605. #if SOCK_TCP_DEBUG >= 2
  606.     sock_print("sock_tcp_send",sp);
  607. #endif
  608.  
  609.     /* socket hasn't finished connecting yet */
  610.     if (sp->sstate == SOCK_STATE_CONNECTING)
  611.     {
  612. #if SOCK_TCP_DEBUG >= 5
  613.         dprintf("sock_tcp_send: connection still in progress\n");
  614. #endif
  615.         if (sp->nonblocking)
  616.             return(sock_err(EALREADY));
  617.             
  618.         /* async connect and sync send? */
  619. #if SOCK_TCP_DEBUG >= 5
  620.         dprintf("sock_tcp_send: spinning on connect\n");
  621. #endif
  622.         while(sp->sstate == SOCK_STATE_CONNECTING)
  623.         {
  624. #if SOCK_TCP_DEBUG >= 5
  625.             tcpCheckNotify();
  626. #endif
  627.             SPIN(false,SP_MISC,0L)
  628.         }
  629. #if SOCK_TCP_DEBUG >= 5
  630.         dprintf("sock_tcp_send: done spinning\n");
  631. #endif
  632.     }
  633.         
  634.     /* socket is not connected */
  635.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  636.     {
  637.         /* see if a previous operation failed */
  638.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  639.         {
  640.             (void) sock_err(sp->asyncerr);
  641.             sp->asyncerr = 0;
  642.             return(-1);
  643.         }
  644.  
  645.         /* I guess he just forgot */
  646.         return(sock_err(ENOTCONN));        
  647.     }
  648.     
  649.     if ( (xTCPBytesWriteable(sp) < count) && (sp->nonblocking) )
  650.         return sock_err(EWOULDBLOCK);
  651.         
  652.     bytes=count;    /* save count before we nuke it */
  653.     sock_memset(wdsarray,0,TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  654.     thiswds = wdsarray;
  655.     wdsnum = 0;
  656.     
  657.     while (count > 0)
  658.         {    
  659.         /* make sure the thing that just finished worked ok */
  660.         if (sp->asyncerr != 0)
  661.             {
  662.             (void) sock_err(sp->asyncerr);
  663.             sp->asyncerr = 0;
  664.             return(-1);
  665.             }
  666.  
  667. /*
  668.  * for deBUGging: try replacing TCP_MAX_MSG with a small value (like 7) so
  669.  * you can test that the loop won't choke while waiting for writes to finish
  670.  */
  671.         towrite=min(count,TCP_MAX_MSG);
  672.         
  673.         /* find a clean wds */
  674.         
  675.         while ( thiswds->length != 0 )
  676.             {
  677.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  678.             if (wdsnum)
  679.                 thiswds++;
  680.             else
  681.                 thiswds = wdsarray;
  682.             SPIN(false,SP_TCP_WRITE,count);    /* spin once */
  683.             }
  684.         
  685.         /* find a clean pb */
  686.         
  687.         if (!(pb=sock_fetch_pb(sp)))
  688.             return sock_err(ENOMEM);
  689.             
  690.         
  691.         thiswds->length = (short)towrite;
  692.         thiswds->ptr=buffer;
  693.                 
  694.         xTCPSend(pb,(wdsEntry *)thiswds,(count <= TCP_MAX_MSG), /* push */
  695.                 flags & MSG_OOB,    /*urgent*/
  696.                 sock_tcp_send_done);
  697.                 
  698.         SPIN(false,SP_TCP_WRITE,count);
  699.         count -= towrite;
  700.         buffer += towrite;
  701.         }
  702.         
  703.     SPIN(pb->ioResult == inProgress,SP_TCP_WRITE,0);
  704.     
  705.     if ( pb->ioResult == noErr )
  706.         {
  707.         return(bytes);
  708.         }
  709.     else
  710.         return(sock_err(pb->ioResult));
  711. }
  712.  
  713.  
  714. /*
  715.  *    sock_tcp_can_write() - returns non-zero if a write will not block
  716.  * Very lousy check. Need to check (send window)-(unack data).
  717.  */
  718. int sock_tcp_can_write(
  719.     SocketPtr sp)
  720. {
  721.     return (sp->sstate == SOCK_STATE_CONNECTED);
  722. }
  723.  
  724. /*
  725.  *    sock_tcp_close() - close down a socket being careful about i/o in progress
  726.  */
  727. int sock_tcp_close(
  728.     SocketPtr sp)
  729. {
  730.     OSErr io;
  731.     TCPiopb    *pb;
  732.  
  733.  void sock_flush_out(SocketPtr);
  734.  void sock_flush_in(SocketPtr);
  735.     
  736. #if SOCK_TCP_DEBUG >= 2
  737.     sock_print("sock_tcp_close ",sp);
  738. #endif
  739.     
  740.     if (!(pb=sock_fetch_pb(sp)))
  741.         return sock_err(ENOMEM);
  742.         
  743.     sock_flush_out(sp);
  744.     
  745.     /* close the stream */ 
  746.     io = xTCPClose(pb,(TCPIOCompletionProc)(-1));
  747.     
  748. #if SOCK_TCP_DEBUG >= 5
  749.     dprintf("sock_tcp_close: xTCPClose returns %d\n",io);
  750. #endif
  751.  
  752.     switch (io)
  753.     {
  754.         case noErr:
  755.         case connectionClosing:
  756.             break;
  757.         case connectionDoesntExist:
  758.         case connectionTerminated:
  759.             break;
  760.         case invalidStreamPtr:
  761.         default:
  762.             return(sock_err(io));
  763.     }
  764.     
  765.     sock_flush_in(sp);
  766.     
  767.     /* destroy the stream */ 
  768.     if ((io = xTCPRelease(pb)) != noErr)
  769.     {
  770. #if SOCK_TCP_DEBUG >= 5
  771.         dprintf("sock_tcp_close: xTCPRelease error %d\n",io);
  772. #endif
  773.         return(sock_err(io));
  774.     }
  775.     /* check for errors from async writes etc */
  776.     if (( sp->asyncerr != noErr ) && ( sp->asyncerr != connectionTerminated ))
  777.     {
  778. #if SOCK_TCP_DEBUG >= 5
  779.         dprintf("sock_tcp_close: asyncerr %d\n",sp->asyncerr);
  780. #endif
  781.         return(sock_err(sp->asyncerr));
  782.     }
  783.     return(0);
  784. }
  785.  
  786. static void sock_flush_out(SocketPtr sp) {
  787.     while (xTCPWriteBytesLeft(sp)>0) {
  788.         (*spinroutine)(SP_MISC,0L);
  789.         }
  790.     }
  791.  
  792. static void sock_flush_in(SocketPtr sp) {
  793.     TCPiopb    *pb;
  794.     rdsEntry    rdsarray[TCP_MAX_WDS+1];
  795.     int        passcount;
  796.     const int maxpass =4;
  797.     
  798.     if (!(pb = sock_fetch_pb(sp)))
  799.         return;    
  800.         
  801.     for (passcount=0;passcount<maxpass;passcount++) {
  802.         if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  803.             xTCPBufReturn(pb,rdsarray,0);
  804.             (*spinroutine)(SP_MISC,0L);
  805.             }
  806.         else
  807.             break;
  808.         }
  809.         
  810.     if (passcount == maxpass) {        /* remote side isn't being nice */
  811.         (void)xTCPAbort(pb);        /* then try again */
  812.         
  813.         for (passcount=0;passcount<maxpass;passcount++) {
  814.             if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  815.                 xTCPBufReturn(pb,rdsarray,0);
  816.                 (*spinroutine)(SP_MISC,0L);
  817.                 }
  818.             else
  819.                 break;
  820.             }
  821.         }
  822.     }
  823.     
  824.  
  825. #pragma segment SOCK_RESIDENT
  826. /*
  827.  * Interrupt routines - MUST BE IN A RESIDENT SEGMENT! Most important to 
  828.  * MacApp programmers
  829.  */
  830. pascal void sock_tcp_notify(
  831.     StreamPtr tcpStream,
  832.     unsigned short eventCode,
  833.     Ptr userDataPtr,
  834.     unsigned short terminReason,
  835.     struct ICMPReport *icmpMsg)
  836.     {
  837. #pragma unused (userDataPtr,terminReason,icmpMsg)
  838.     register     StreamHashEntPtr    shep;
  839.     
  840.  
  841.     shep = sock_find_shep(tcpStream);
  842.     if ( shep )
  843.         {
  844.         SocketPtr    sp = shep->socket;
  845.         
  846.         if ( eventCode == TCPClosing )
  847.             {
  848.             sp->sstate = SOCK_STATE_CLOSING;
  849.             }
  850.         else if ( eventCode == TCPTerminate )
  851.             {
  852.             sp->sstate = SOCK_STATE_UNCONNECTED;
  853.             }
  854.         }
  855.     }
  856.  
  857. static void sock_tcp_connect_done(TCPiopb *pb)
  858.     {
  859.     SocketPtr sp;
  860.     
  861.     sp = sock_find_shep(pb->tcpStream)->socket;
  862.     
  863.     if (pb->ioResult == noErr )
  864.         {
  865.         sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  866.         sp->sa.sin_port = pb->csParam.open.localPort;
  867.         sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  868.         sp->peer.sin_port = pb->csParam.open.remotePort;
  869.         sp->sstate = SOCK_STATE_CONNECTED;
  870.         sp->asyncerr = noErr;
  871.         }
  872.     }
  873.  
  874.  
  875. static void
  876. sock_tcp_listen_done(TCPiopb *pb)
  877. {    
  878.     SocketPtr sp;
  879.  
  880.     sp = sock_find_shep(pb->tcpStream)->socket;
  881.     
  882.     switch(pb->ioResult)
  883.     {
  884.         case noErr:
  885.             sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  886.             sp->peer.sin_port = pb->csParam.open.remotePort;
  887.             sp->sstate = SOCK_STATE_LIS_CON;
  888.             sp->asyncerr = 0;
  889.             break;
  890.             
  891.         case openFailed:
  892.         case invalidStreamPtr:
  893.         case connectionExists:
  894.         case duplicateSocket:
  895.         case commandTimeout:
  896.         default:                
  897.             sp->sstate = SOCK_STATE_UNCONNECTED;
  898.             sp->asyncerr = pb->ioResult;
  899.             break;
  900.     }
  901. }
  902.  
  903. static void
  904. sock_tcp_recv_done(
  905.     TCPiopb *pb)
  906.     {
  907.     register    readin;
  908.     register    SocketPtr    sp;
  909.  
  910.     sp = sock_find_shep( pb->tcpStream )->socket;;
  911.     
  912.     if (pb->ioResult == noErr)
  913.         {
  914.         readin = pb->csParam.receive.rcvBuffLen;
  915.         sp -> recvBuf += readin;
  916.         sp -> recvd   += readin;
  917.         sp -> torecv  -= readin;
  918.         if ( sp -> torecv )
  919.             {
  920.             xTCPRcv(pb,sp->recvBuf,min(sp -> torecv,TCP_MAX_MSG),1,
  921.                     (TCPIOCompletionProc)sock_tcp_recv_done);
  922.             }
  923.         }
  924.     }
  925.  
  926.  
  927. static void
  928. sock_tcp_send_done(
  929.     TCPiopb *pb)
  930. {    
  931.     SocketPtr sp;
  932.  
  933.     sp = sock_find_shep(pb->tcpStream)->socket;
  934.     
  935.     switch(pb->ioResult)
  936.     {
  937.         case noErr:
  938.             ((wdsEntry *)(pb->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  939.             break;
  940.             
  941.         case ipNoFragMemErr:
  942.         case connectionClosing:
  943.         case connectionTerminated:
  944.         case connectionDoesntExist:
  945.             sp->sstate = SOCK_STATE_UNCONNECTED;
  946.             sp->asyncerr = ENOTCONN;
  947.             break;
  948.             
  949.         case ipDontFragErr:
  950.         case invalidStreamPtr:
  951.         case invalidLength:
  952.         case invalidWDS:
  953.         default:
  954.             sp->sstate = SOCK_STATE_UNCONNECTED;
  955.             sp->asyncerr = pb->ioResult;
  956.             break;
  957.     }
  958.     
  959. }
  960.  
  961.