home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / GUSI 1.4.1 / GUSI / GUSIUDP.cp < prev    next >
Encoding:
Text File  |  1994-02-25  |  11.1 KB  |  440 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIUDP.cp        -    UDP Datagram Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by 
  7.     
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.           
  11. Language    :    MPW C/C++
  12.  
  13. $Log: GUSIUDP.cp,v $
  14. Revision 1.1  1994/02/25  02:31:00  neeri
  15. Initial revision
  16.  
  17. Revision 0.5  1994/02/15  00:00:00  neeri
  18. udp_read_ahead_done didn't record the packet source
  19.  
  20. Revision 0.4  1993/06/21  00:00:00  neeri
  21. forgot to reset asyncerr
  22.  
  23. Revision 0.3  1993/06/17  00:00:00  neeri
  24. getsockname() didn't work for some sorts of bind()
  25.  
  26. Revision 0.2  1992/09/14  00:00:00  neeri
  27. select() didn't return enough goodies.
  28.  
  29. Revision 0.1  1992/08/25  00:00:00  neeri
  30. Started putting some real work in
  31.  
  32. *********************************************************************/
  33.  
  34. #include "GUSIINET_P.h"
  35.  
  36. /********************** Completion procedures ***********************/
  37.  
  38. #pragma segment GUSIResident
  39.  
  40. void udp_read_ahead_done(AnnotatedPB *pb)
  41. {
  42.     UDPSocket *        sock    =    (UDPSocket *) pb->Owner();
  43.     UDPiopb *        udp    =    pb->UDP();
  44.  
  45.     if (!udp->ioResult) {
  46.         sock->recvBuf    = udp->csParam.receive.rcvBuff;
  47.         sock->recvd        = udp->csParam.receive.rcvBuffLen;
  48.     } else {
  49.         sock->recvBuf    = nil;
  50.         sock->recvd        = 0;
  51.     }
  52.     sock->asyncerr                 =     udp->ioResult;
  53.     sock->peer.sin_len             = sizeof(struct sockaddr_in);
  54.     sock->peer.sin_family         = AF_INET;
  55.     sock->peer.sin_addr.s_addr    =    udp->csParam.receive.remoteHost;
  56.     sock->peer.sin_port            =    udp->csParam.receive.remotePort;
  57. }
  58.  
  59. void udp_send_done(AnnotatedPB *pb)
  60. {    
  61.     UDPiopb *        udp    =    pb->UDP();
  62.     
  63.     ((miniwds *)udp->csParam.send.wdsPtr)->terminus    = udp->ioResult;
  64.     ((miniwds *)udp->csParam.send.wdsPtr)->ptr        = nil;
  65. }
  66.  
  67. #pragma segment GUSIINET
  68.  
  69. /************************ UDPSocket members *************************/
  70.  
  71. UDPSocket::UDPSocket()
  72.     :    INETSocket()
  73. {
  74.     sstate = SOCK_STATE_NO_STREAM;
  75. }
  76.  
  77. UDPSocket::UDPSocket(StreamPtr stream)
  78.     :    INETSocket(stream)
  79. {
  80.     OSErr            err;
  81.     UDPiopb *    pb;
  82.  
  83.     pb                                                = GetPB();
  84.     pb->ioCompletion                            = UDPIOCompletionProc(udp_read_ahead_done);
  85.     pb->csCode                                     = UDPRead;
  86.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  87.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  88.  
  89.     /* We know that there is a read pending, so we make a synchronous call */
  90.     
  91.     if (err = PBControlSync(ParmBlkPtr(pb)))
  92.         TCP_error(err);
  93.     
  94.     if (!pb->ioResult) {
  95.         recvBuf    = pb->csParam.receive.rcvBuff;
  96.         recvd        = pb->csParam.receive.rcvBuffLen;
  97.     } else {
  98.         recvBuf    = nil;
  99.         recvd        = 0;
  100.     }
  101.     asyncerr                 =    pb->ioResult;
  102.     peer.sin_len             = sizeof(struct sockaddr_in);
  103.     peer.sin_family         = AF_INET;
  104.     peer.sin_addr.s_addr    =    pb->csParam.receive.remoteHost;
  105.     peer.sin_port            =    pb->csParam.receive.remotePort;
  106. }
  107.  
  108. UDPSocket::~UDPSocket()
  109. {
  110.     UDPiopb *    pb;
  111.     
  112.     if (sstate == SOCK_STATE_NO_STREAM)
  113.         return;
  114.     
  115.     pb                    = GetPB();    
  116.     pb->csCode         = UDPRelease;
  117.     
  118.     if (!PBControlSync(ParmBlkPtr(pb)))
  119.         DisposPtr(pb->csParam.create.rcvBuff);
  120. }
  121.  
  122. UDPiopb * UDPSocket::GetPB()
  123. {
  124.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  125.     pb->UDP()->ioCRefNum =    INETSockets.Driver();
  126.     pb->UDP()->udpStream    =    stream;
  127.     
  128.     pb->SetOwner(this);
  129.     
  130.     return pb->UDP();
  131. }
  132.  
  133. unsigned long UDPSocket::Available()
  134. {
  135.     return (asyncerr == inProgress) ? 0 : recvd;
  136. }
  137.  
  138. int UDPSocket::NewStream()
  139. {
  140.     OSErr            err;
  141.     UDPiopb *    pb;
  142.  
  143.     pb                                        = GetPB();
  144.     pb->csCode                             = UDPCreate;
  145.     pb->csParam.create.rcvBuff     = (char *)NewPtr(STREAM_BUFFER_SIZE);
  146.     pb->csParam.create.rcvBuffLen    = STREAM_BUFFER_SIZE;
  147.     pb->csParam.create.notifyProc    = NULL;
  148.     pb->csParam.create.localPort     = sa.sin_port;
  149.     
  150.     if (err = PBControlSync(ParmBlkPtr(pb)))
  151.         return TCP_error(err);
  152.         
  153.     stream         = pb->udpStream;
  154.     sa.sin_port = pb->csParam.create.localPort;
  155.     
  156.     sstate     = SOCK_STATE_UNCONNECTED;
  157.     recvd     = 0;
  158.     recvBuf     = 0;
  159.     asyncerr    = inProgress;
  160.  
  161.     return ReadAhead();
  162. }
  163.  
  164. int UDPSocket::FlushReadAhead()
  165. {
  166.     OSErr            err;
  167.     UDPiopb *    pb;
  168.  
  169.     /* flush the read-ahead buffer if its not from our new friend */
  170.     pb                                        = GetPB();
  171.     pb->ioCompletion                    = nil;
  172.     pb->csCode                             = UDPBfrReturn;
  173.     pb->csParam.receive.rcvBuff    = recvBuf;
  174.     recvBuf                                = 0;
  175.     recvd                                   = 0;
  176.     
  177.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  178.         return TCP_error(err);
  179.     else
  180.         return 0;
  181. }
  182.  
  183. int UDPSocket::ReadAhead()
  184. {
  185.     OSErr            err;
  186.     UDPiopb *    pb;
  187.  
  188.     pb                                                = GetPB();
  189.     pb->ioCompletion                            = UDPIOCompletionProc(udp_read_ahead_done);
  190.     pb->csCode                                     = UDPRead;
  191.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  192.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  193.     asyncerr                                        = 1;
  194.     
  195.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  196.         return TCP_error(asyncerr = err);
  197.     
  198.     return 0;
  199. }
  200.  
  201. /*
  202.  *    getsockname(name, namelen)
  203.  *
  204.  *        getsockname returns the current name for the  socket.
  205.  *        Namelen should  be initialized to
  206.  *        indicate the amount of space pointed to by name.  On  return
  207.  *        it contains the actual size of the name returned (in bytes).
  208.  *        
  209.  *        A 0 is returned if the call succeeds, -1 if it fails.
  210.  */
  211.  
  212. int UDPSocket::getsockname(void *name, int *namelen)
  213. {
  214.     if (sstate == SOCK_STATE_NO_STREAM)
  215.         if (NewStream())
  216.             return -1;
  217.  
  218.     return INETSocket::getsockname(name, namelen);
  219. }
  220.  
  221. /*
  222.  *    UDPSocket::connect - initiate a connection on a MacTCP socket
  223.  *
  224.  *        This  call specifies the address to  which  datagrams
  225.  *        are  to  be  sent, and the only address from which datagrams
  226.  *        are to be received.  
  227.  *             
  228.  *        UDP sockets may use connect() multiple times to change
  229.  *        their association. UDP sockets may dissolve the association
  230.  *        by connecting to an invalid address, such as a null
  231.  *        address.
  232.  *        
  233.  *        If the connection or binding succeeds, then 0 is returned.
  234.  *        Otherwise a -1 is returned, and a more specific error code
  235.  *        is stored in errno.
  236.  *        
  237.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  238.  *        
  239.  *        EHOSTUNREACH        The TCP connection came up half-way and 
  240.  *                          then failed.
  241.  */
  242.  
  243. int UDPSocket::connect(void * address, int addrlen)
  244. {
  245.     struct sockaddr_in *    addr    = (struct sockaddr_in *) address;
  246.         
  247.     if (addrlen != sizeof(struct sockaddr_in))
  248.         return GUSI_error(EINVAL);
  249.  
  250.     if (addr->sin_family != AF_INET)
  251.         return GUSI_error(EAFNOSUPPORT);
  252.     
  253.     /* make the stream if its not made already */
  254.     if (sstate == SOCK_STATE_NO_STREAM) {
  255.         if (NewStream())
  256.             return -1;
  257.     } else if (recvBuf)
  258.         if (FlushReadAhead())
  259.             return -1;
  260.     
  261.     /* record our peer */
  262.     peer.sin_len             = sizeof(struct sockaddr_in);
  263.     peer.sin_addr.s_addr    = addr->sin_addr.s_addr;
  264.     peer.sin_port             = addr->sin_port;
  265.     peer.sin_family         = AF_INET;
  266.     sstate                     = SOCK_STATE_CONNECTED;
  267.     
  268.     return 0;
  269. }
  270.  
  271. /*    
  272.  *    UDPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  273.  *        
  274.  *        recv() and recvfrom() attempt to receive a message (ie a datagram) 
  275.  *        on the socket s. 
  276.  *
  277.  *        from returns the address of the socket which sent the message.
  278.  *        fromlen is the usual value-result length parameter. 
  279.  *        
  280.  *        Typically, read() is used with a TCP stream and recv() with
  281.  *        UDP where the idea of a message makes more sense. But in fact,
  282.  *        read() and recv() are equivalent.
  283.  *        
  284.  *        If a message (ie. datagram) is too long to fit in the supplied 
  285.  *        buffer, excess bytes will be discarded..
  286.  *
  287.  *        If no messages are available at the socket, the receive call
  288.  *        waits for a message to arrive, unless the socket is non-
  289.  *        blocking in which case -1 is returned with errno set to 
  290.  *        EWOULDBLOCK.
  291.  *
  292.  *        Flags is ignored.
  293.  *        
  294.  *        If successful, the number of bytes actually received is
  295.  *        returned. Otherwise, a -1 is returned and the global variable
  296.  *        errno is set to indicate the error. 
  297.  *        
  298.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  299.  */
  300.  
  301. int UDPSocket::recvfrom(void * buffer, int buflen, int, void * from, int * fromlen)
  302. {
  303.     /* make the stream if its not made already */
  304.     if (sstate == SOCK_STATE_NO_STREAM)
  305.         if (NewStream())
  306.             return -1;
  307.             
  308.     /* dont block a non-blocking socket */
  309.     if (nonblocking && asyncerr == 1)
  310.         return GUSI_error(EWOULDBLOCK);
  311.     
  312.     SPIN(asyncerr == 1, SP_DGRAM_READ,0);
  313.  
  314.     if (asyncerr!=noErr)
  315.         return TCP_error(asyncerr);
  316.  
  317.     /* return the data to the user - truncate the packet if necessary */
  318.     buflen = min(buflen,recvd);    
  319.     BlockMove(recvBuf, buffer, buflen);
  320.  
  321.     if (from && *fromlen >= sizeof(struct sockaddr_in)) {
  322.         *(struct sockaddr_in *) from = peer;
  323.         *fromlen = sizeof (struct sockaddr_in);
  324.     }    
  325.     
  326.     /* continue the read-ahead - errors which occur */
  327.     /* here will show up next time around */
  328.     
  329.     FlushReadAhead();
  330.     ReadAhead();
  331.     
  332.     return buflen;
  333. }
  334.  
  335. /*
  336.  *    UDPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  337.  *        sendto() is used to transmit a message to another 
  338.  *        socket on the socket s.
  339.  *
  340.  *        Typically, write() is used with a TCP stream and send() with
  341.  *        UDP where the idea of a message makes more sense. But in fact,
  342.  *        write() and send() are equivalent.
  343.  *
  344.  *    Write() and send() operations are completed as soon as the
  345.  *    data is placed on the transmission queue.
  346.  *
  347.  *        The address of the target is given by to.
  348.  *
  349.  *        The message must be short enough to fit into one datagram.
  350.  *        
  351.  *        Buffer space must be available to hold the message to be 
  352.  *        transmitted, regardless of its non-blocking I/O state.
  353.  *
  354.  *        Flags is ignored.
  355.  *        
  356.  *        These calls return the number of bytes sent, or -1 if an error 
  357.  *        occurred.
  358.  *        
  359.  *        EINVAL           The sum of the iov_len values in the iov array was
  360.  *                                 greater than 65535 (TCP) or 65507 (UDP) or there
  361.  *                       were too many entries in the array (16 for TCP or
  362.  *                       6 for UDP).
  363.  *
  364.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  365.  *        
  366.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  367.  *
  368.  *        ENOBUFS          The transmit queue is full. (UDP)
  369.  */
  370.  
  371. int UDPSocket::sendto(void * buffer, int count, int, void * to, int)
  372. {
  373.     miniwds      awds;
  374.     OSErr        err;
  375.     UDPiopb *    pb;
  376.  
  377.     /* make the stream if its not made already */
  378.     if (sstate == SOCK_STATE_NO_STREAM)
  379.         if (NewStream())
  380.             return -1;
  381.     
  382.     if (count > UDP_MAX_MSG)
  383.         return GUSI_error(EMSGSIZE);
  384.         
  385.     awds.terminus = 0;
  386.     awds.length = count;
  387.     awds.ptr = (char *) buffer;
  388.     
  389.     // if no address passed, hope we have one already in peer field
  390.     if (to == NULL)
  391.         if (peer.sin_len)
  392.             to = &peer;
  393.         else
  394.             return GUSI_error(EHOSTUNREACH);
  395.     
  396.     pb                                        = GetPB();
  397.     pb->ioCompletion                    = UDPIOCompletionProc(udp_send_done);
  398.     pb->csCode                             = UDPWrite;
  399.     pb->csParam.send.remoteHost     = ((struct sockaddr_in *)to)->sin_addr.s_addr;
  400.     pb->csParam.send.remotePort     = ((struct sockaddr_in *)to)->sin_port;
  401.     pb->csParam.send.wdsPtr         = (Ptr)&awds;
  402.     pb->csParam.send.checkSum         = true;
  403.     pb->csParam.send.sendLength     = 0/* must be zero */;
  404.     
  405.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  406.         return TCP_error(err);
  407.     
  408.     // get sneaky. compl. proc sets ptr to nil on completion, and puts result code in
  409.     // terminus field.
  410.     
  411.     SPIN(awds.ptr != NULL, SP_DGRAM_WRITE, count);
  412.     
  413.     if (awds.terminus < 0)
  414.         return TCP_error(awds.terminus);
  415.     else
  416.         return count;
  417. }
  418.  
  419. int UDPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  420. {
  421.     int        goodies     =     0;
  422.     
  423.     if (canRead || canWrite)
  424.         if (sstate == SOCK_STATE_NO_STREAM)
  425.             NewStream();
  426.                 
  427.     if (canRead)
  428.         if (asyncerr != 1) {
  429.             *canRead = true;
  430.             ++goodies;
  431.         }
  432.         
  433.     if (canWrite) {
  434.         *canWrite = true;
  435.         ++goodies;
  436.     }
  437.     
  438.     return goodies;
  439. }
  440.