home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / GUSI 1.4.1 / GUSI / GUSIINET.cp < prev    next >
Encoding:
Text File  |  1994-05-01  |  13.7 KB  |  596 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIINET.cp        -    TCP/IP Sockets, general routines
  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: GUSIINET.cp,v $
  14. Revision 1.2  1994/05/01  23:39:54  neeri
  15. Defer PB allocation.
  16.  
  17. Revision 1.1  1994/03/08  22:06:58  neeri
  18. Initial revision
  19.  
  20. Revision 0.7  1993/10/31  00:00:00  neeri
  21. Deferred opening of MacTCP Services
  22.  
  23. Revision 0.6  1993/07/17  00:00:00  neeri
  24. htonl and friends
  25.  
  26. Revision 0.5  1993/02/07  00:00:00  neeri
  27. New configuration technique
  28.  
  29. Revision 0.4  1993/01/31  00:00:00  neeri
  30. Inetd support
  31.  
  32. Revision 0.3  1992/08/23  00:00:00  neeri
  33. INETSocket::Available()
  34.  
  35. Revision 0.2  1992/08/20  00:00:00  neeri
  36. Wrote most of the functions
  37.  
  38. Revision 0.1  1992/08/16  00:00:00  neeri
  39. Split
  40.  
  41. *********************************************************************/
  42.  
  43. #include "GUSIINET_P.h"
  44. #include <machine/endian.h>
  45.  
  46. #pragma segment GUSIINET
  47.  
  48. /***************************** Globals ******************************/
  49.  
  50. #define NUM_PBS    32
  51.  
  52. INETSocketDomain    INETSockets;
  53.  
  54. /***************************** Peanuts ******************************/
  55.  
  56. unsigned long (htonl)(unsigned long h)
  57. {
  58.     return h;
  59. }
  60.  
  61. unsigned short    (htons)(unsigned short h)
  62. {
  63.     return h;
  64. }
  65.  
  66. unsigned long (ntohl)(unsigned long n)
  67. {
  68.     return n;
  69. }
  70.  
  71. unsigned short    (ntohs)(unsigned short n)
  72. {
  73.     return n;
  74. }
  75.  
  76. /***************** Our resident MacTCP error expert *****************/
  77.  
  78. /*
  79.  * Convert a MacTCP err code into a unix error code.
  80.  */
  81.  
  82. int TCP_error(int MacTCPerr)
  83. {
  84.     switch ( MacTCPerr ) {
  85.     case 0:
  86.         return 0;
  87.     case ipBadLapErr:
  88.     case ipBadCnfgErr:
  89.     case ipNoCnfgErr:
  90.     case ipLoadErr:
  91.     case ipBadAddr:
  92.         errno = ENXIO;            /* device not configured */    /* a cheap cop out */
  93.         break;
  94.     case connectionClosing:
  95.         errno = ESHUTDOWN;        /* Can't send after socket shutdown */
  96.         break;
  97.     case connectionExists:
  98.         errno = EISCONN;        /* Socket is already connected */
  99.         break;
  100.     case connectionTerminated:
  101.         errno = ENOTCONN;        /* Connection reset by peer */  /* one of many possible */
  102.         break;
  103.     case openFailed:
  104.         errno = ECONNREFUSED;    /* Connection refused */
  105.         break;
  106.     case duplicateSocket:        /* technically, duplicate port */
  107.         errno = EADDRINUSE;        /* Address already in use */
  108.         break;
  109.     case ipDestDeadErr:
  110.         errno = EHOSTDOWN;        /* Host is down */
  111.         break;
  112.     case ipRouteErr:
  113.         errno = EHOSTUNREACH;    /* No route to host */
  114.         break;
  115.     default:
  116.         errno = MacTCPerr > 0 ? MacTCPerr : EFAULT;        /* cop out; an internal err, unix err, or no err */
  117.         break;
  118.     }
  119.  
  120.     return -1;
  121. }
  122.  
  123. /************************ INETD support ************************/
  124.  
  125. pascal OSErr 
  126. TCPPossession(AppleEvent* messagein, AppleEvent* /*reply*/, long /*refIn*/)
  127. {
  128.     AEDesc        streamDesc;
  129.     OSErr            theErr;
  130.     TCPSocket *    sock;
  131.  
  132.     if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
  133.         HLock(streamDesc.dataHandle);
  134.         sock = new TCPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
  135.         HUnlock(streamDesc.dataHandle);
  136.         Sockets.Possess(0, sock);
  137.         Sockets.Possess(1, sock);
  138.         Sockets.Possess(2, sock);
  139.         
  140.         theErr = AEDisposeDesc(&streamDesc);
  141.     }
  142.  
  143.     return theErr;
  144. }
  145.  
  146. pascal OSErr 
  147. UDPPossession(AppleEvent* messagein, AppleEvent* /*reply*/, long /*refIn*/)
  148. {
  149.     AEDesc        streamDesc;
  150.     OSErr            theErr;
  151.     UDPSocket *    sock;
  152.  
  153.     if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
  154.         HLock(streamDesc.dataHandle);
  155.         sock = new UDPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
  156.         HUnlock(streamDesc.dataHandle);
  157.         Sockets.Possess(0, sock);
  158.         Sockets.Possess(1, sock);
  159.         Sockets.Possess(2, sock);
  160.         
  161.         theErr = AEDisposeDesc(&streamDesc);
  162.     }
  163.  
  164.     return theErr;
  165. }
  166.  
  167. /************************ INETSocket members ************************/
  168.  
  169. INETSocket::INETSocket()
  170.     : Socket()
  171. {
  172.     bzero(&sa, sizeof(struct sockaddr_in));
  173.     bzero(&peer, sizeof(struct sockaddr_in));
  174.     
  175.     sa.sin_family    = AF_INET;
  176.     sa.sin_len        = sizeof(struct sockaddr_in);
  177.     status            = SOCK_STATUS_USED;
  178.     nonblocking        = false;    
  179.     
  180.     INETSockets.OpenSocket();
  181. }
  182.  
  183. INETSocket::INETSocket(StreamPtr stream)
  184.     : Socket(), stream(stream)
  185. {
  186.     sa.sin_family    = AF_INET;
  187.     sa.sin_len        = sizeof(struct sockaddr_in);
  188.     peer.sin_family= AF_INET;
  189.     peer.sin_len    = sizeof(struct sockaddr_in);
  190.     status            = SOCK_STATUS_USED;
  191.     sstate            = SOCK_STATE_CONNECTED;
  192.     nonblocking        = false;    
  193.     
  194.     INETSockets.OpenSocket();
  195. }
  196.  
  197. INETSocket::~INETSocket()
  198. {
  199.     INETSockets.CloseSocket();
  200. }
  201.  
  202. unsigned long INETSocket::Available()
  203. {
  204.     return 0;
  205. }
  206.  
  207. /*
  208.  *    bind(name, namelen)
  209.  *
  210.  *        bind requests that the name (ip address and port) pointed to by 
  211.  *        name be assigned to the socket.
  212.  *        
  213.  *        The return value is 0 on success or -1 if an error occurs,
  214.  *        in which case global variable errno is set to one of:
  215.  *
  216.  *        EAFNOSUPPORT        The address family in name is not AF_INET.
  217.  *        
  218.  *        EINVAL              The socket is already bound to an address.
  219.  *        
  220.  *        EADDRNOTAVAIL       The specified address is  not  available
  221.  *                             from the local machine. ie. the address
  222.  *                            portion of name was not this machine's address.
  223.  *
  224.  *        MacTCP does not separate name binding and connection establishment.
  225.  *        Therefore the port number is not verified, just stored for later use.
  226.  *
  227.  *        If a specific local port is not required, bind is optional in this
  228.  *        implementation.
  229.  */    
  230.  
  231. int INETSocket::bind(void * addr, int namelen)
  232. {
  233.     struct sockaddr_in *name    =    (struct sockaddr_in *)addr;
  234.         
  235.     if (namelen < sizeof(struct sockaddr_in))
  236.         return GUSI_error(EINVAL);
  237.  
  238.     if (name->sin_family != AF_INET)
  239.         return GUSI_error(EAFNOSUPPORT);
  240.  
  241.     if (sa.sin_port != 0) /* already bound */
  242.         return GUSI_error(EINVAL);
  243.  
  244.     /*
  245.      *    If client passed a local IP address, assure it is the right one
  246.      */
  247.     if (name->sin_addr.s_addr != 0) 
  248.     {
  249.         struct GetAddrParamBlock pbr;
  250.         
  251.         pbr.ioCRefNum     = INETSockets.Driver();
  252.         pbr.csCode         = ipctlGetAddr;
  253.         
  254.         if (PBControlSync(ParmBlkPtr(&pbr)))
  255.             return GUSI_error(ENETDOWN);
  256.         
  257.         if (name->sin_addr.s_addr != pbr.ourAddress)
  258.             return GUSI_error(EADDRNOTAVAIL);
  259.     }
  260.  
  261.     /*
  262.      *    NOTE: can't check a TCP port for EADDRINUSE
  263.      *    just save the address and port away for connect or listen or...
  264.      */
  265.     sa.sin_addr.s_addr     = name->sin_addr.s_addr;
  266.     sa.sin_port             = name->sin_port;
  267.     
  268.     return 0;
  269. }
  270.  
  271. /*
  272.  *    getsockname(name, namelen)
  273.  *
  274.  *        getsockname returns the current name for the  socket.
  275.  *        Namelen should  be initialized to
  276.  *        indicate the amount of space pointed to by name.  On  return
  277.  *        it contains the actual size of the name returned (in bytes).
  278.  *        
  279.  *        A 0 is returned if the call succeeds, -1 if it fails.
  280.  */
  281.  
  282. int INETSocket::getsockname(void *name, int *namelen)
  283. {
  284.     if (*namelen < 0)
  285.         return GUSI_error(EINVAL);
  286.  
  287.     memcpy(name, &sa, *namelen = min(*namelen, sizeof(struct sockaddr_in)));
  288.     return 0;
  289. }
  290.  
  291. /*
  292.  *    getpeername(name, namelen)
  293.  *
  294.  *        getpeername returns the name of the peer connected to socket.
  295.  *
  296.  *        The  int  pointed  to  by the namelen parameter
  297.  *        should be  initialized  to  indicate  the  amount  of  space
  298.  *        pointed  to  by name.  On return it contains the actual size
  299.  *        of the name returned (in bytes).  The name is  truncated  if
  300.  *        the buffer provided is too small.
  301.  *        
  302.  *        A 0 is returned if the call succeeds, -1 if it fails.
  303.  */
  304.  
  305. int INETSocket::getpeername(void *name, int *namelen)
  306. {
  307.     if (*namelen < 0)
  308.         return GUSI_error(EINVAL);
  309.  
  310.     memcpy(name, &peer, *namelen = min(*namelen, sizeof(struct sockaddr_in)));
  311.     return 0;
  312. }
  313.  
  314. /*
  315.  *    shutdown(how)
  316.  *
  317.  *        shutdown call causes all or part of a full-duplex
  318.  *        connection on the socket to be shut down.  If
  319.  *        how is 0, then further receives will be disallowed.  If  how
  320.  *        is  1,  then further sends will be disallowed.  If how is 2,
  321.  *        then further sends and receives will be disallowed.
  322.  *        
  323.  *        A 0 is returned if the call succeeds, -1 if it fails.
  324.  */
  325.  
  326. int INETSocket::shutdown(int how)
  327. {
  328.     switch(how) {
  329.     case 0 : 
  330.         status |= SOCK_STATUS_NOREAD;
  331.         break;
  332.     case 1 : 
  333.         status |= SOCK_STATUS_NOWRITE;
  334.         break;
  335.     case 2 :
  336.         status |= SOCK_STATUS_NOREAD | SOCK_STATUS_NOWRITE;
  337.         break;
  338.     default :
  339.         return GUSI_error(EINVAL);
  340.     }
  341.     
  342.     return 0;
  343. }
  344.  
  345. /*
  346.  *    fcntl() operates on the socket according to the order in cmd:
  347.  *
  348.  *        F_GETFL    returns the descriptor status flags. The only
  349.  *                flag supported is FNDELAY for non-blocking i/o.
  350.  *
  351.  *        F_SETFL    sets descriptor status flags. The only
  352.  *                 flag supported is FNDELAY for non-blocking i/o.
  353.  *
  354.  *        Upon successful completion, the value  returned  depends  on
  355.  *        cmd as follows:
  356.  *         F_GETFL   Value of flags.
  357.  *            F_SETFL   0.
  358.  *
  359.  *        On error, a value of -1  is returned and errno is set to indicate 
  360.  *        the error.
  361.  *
  362.  *        EBADF           s is not a valid open descriptor.
  363.  *
  364.  *        EMFILE          cmd is F_DUPFD and socket descriptor table is full.
  365.  *
  366.  *        EINVAL          cmd is F_DUPFD and arg  is  negative  or
  367.  *                      greater   than   the  maximum  allowable
  368.  *                      number (see getdtablesize).
  369.  */
  370. int INETSocket::fcntl(unsigned int cmd, int arg)
  371. {
  372.     switch(cmd) {
  373.     /*
  374.      *  Get socket status.  This is like getsockopt().
  375.      *  Only supported descriptor status is FNDELAY.
  376.      */
  377.     case F_GETFL : 
  378.         if (nonblocking)
  379.             return FNDELAY;
  380.         else
  381.             return 0;
  382.     /*
  383.      *  Set socket status.  This is like setsockopt().
  384.      *  Only supported descriptor status is FNDELAY.
  385.      */
  386.     case F_SETFL : 
  387.         if (arg & FNDELAY)
  388.             nonblocking = true;
  389.         else
  390.             nonblocking = false;
  391.         
  392.         return 0;
  393.     default:
  394.         return GUSI_error(EOPNOTSUPP);
  395.     }
  396. }
  397.  
  398. int INETSocket::ioctl(unsigned int request, void *argp)
  399. {
  400.     struct ifreq *    ifr;
  401.     int                size;
  402.     
  403.     /*
  404.      * Interpret high order word to find amount of data to be copied 
  405.      * to/from the user's address space.
  406.      */
  407.     size =(request &~(IOC_INOUT | IOC_VOID)) >> 16;
  408.     
  409.     /*
  410.      * Zero the buffer on the stack so the user gets back something deterministic.
  411.      */
  412.     if ((request & IOC_OUT) && size)
  413.         bzero((Ptr)argp, size);
  414.  
  415.     ifr =(struct ifreq *)argp;
  416.     switch(request) {
  417.     /* Non-blocking I/O */
  418.     case FIONBIO:
  419.         nonblocking = (Boolean) *(int *) argp;
  420.         return 0;
  421.     /* Number of bytes on input Q */
  422.     case FIONREAD:
  423.         *(unsigned long *) argp    = Available();
  424.         
  425.         return 0;
  426.     default :
  427.         return GUSI_error(EOPNOTSUPP);
  428.     }
  429. }
  430.  
  431. /********************* INETSocketDomain members *********************/
  432.  
  433. extern "C" void GUSIwithInternetSockets()
  434. {
  435.     INETSockets.DontStrip();
  436. }
  437.  
  438. INETSocketDomain::INETSocketDomain()
  439.     :    SocketDomain(AF_INET)    
  440. {
  441.     GUSIConfiguration    conf;        // GUSIConfig isn't yet guaranteed to work    
  442.     
  443.     driverState        = 1;
  444.     resolverState    = 1;
  445.     drvrRefNum         = 0;
  446.     inetCount        = 0;
  447.     
  448.     /* allocate storage for pbs */
  449.     pbLast = 0;
  450.     pbList = nil;
  451.     
  452.     if (conf.IsDaemon()) {
  453.         if (conf.tcpDaemon)
  454.             AEInstallEventHandler(
  455.                 'INET', 'TSTR', EventHandlerProcPtr(TCPPossession), 0, false);
  456.         else
  457.             AEInstallEventHandler(
  458.                 'INET', 'TSTR', EventHandlerProcPtr(GetOffMyCloud), 0, false);
  459.         
  460.         if (conf.udpDaemon)
  461.             AEInstallEventHandler(
  462.                 'INET', 'USTR', EventHandlerProcPtr(UDPPossession), 0, false);
  463.         else
  464.             AEInstallEventHandler(
  465.                 'INET', 'USTR', EventHandlerProcPtr(GetOffMyCloud), 0, false);
  466.     }
  467. }
  468.  
  469. short INETSocketDomain::Driver()
  470. {
  471.     ParamBlockRec         pb; 
  472.  
  473.     if (driverState == 1) {
  474.         pb.ioParam.ioCompletion    = 0L; 
  475.         pb.ioParam.ioNamePtr     = "\p.IPP"; 
  476.         pb.ioParam.ioPermssn     = fsCurPerm;
  477.         
  478.         driverState                 = PBOpenSync(&pb);
  479.         drvrRefNum                     = pb.ioParam.ioRefNum; 
  480.     }
  481.     
  482.     return driverState ? 0 : drvrRefNum;
  483. }
  484.  
  485. OSErr    INETSocketDomain::Resolver()
  486. {
  487.     if (resolverState == 1)
  488.         resolverState = OpenResolver(nil);
  489.     
  490.     return resolverState;
  491. }
  492.  
  493. /*
  494.  *    INETSocketDomain::socket(type, protocol)
  495.  *
  496.  *        Create a MacTCP socket and return a descriptor.
  497.  *
  498.  *        Type may be SOCK_STREAM to create a TCP socket or 
  499.  *        SOCK_DGRAM to create a UDP socket.
  500.  *                 
  501.  *        Protocol is ignored. (isn't it always?)
  502.  *                 
  503.  *        TCP sockets provide sequenced, reliable, two-way connection
  504.  *        based byte streams.
  505.  *
  506.  *        A TCP socket must be in a connected
  507.  *        state before any data may be sent or received on it. A 
  508.  *        connection to another socket is created with a connect() call
  509.  *        or the listen() and accept() calls.
  510.  *        Once connected, data may be transferred using read() and
  511.  *        write() calls or some variant of the send() and recv()
  512.  *        calls. When a session has been completed a close() may  be
  513.  *        performed.
  514.  *
  515.  *        
  516.  *        A UDP socket supports the exchange of datagrams (connectionless, 
  517.  *        unreliable messages of a fixed maximum length) with  
  518.  *        correspondents named in send() calls. Datagrams are
  519.  *        generally received with recv(), which returns the next
  520.  *        datagram with its return address.
  521.  *
  522.  *        An fcntl() or ioctl() call can be used to enable non-blocking I/O.
  523.  *
  524.  *        The return value is a descriptor referencing the socket or -1
  525.  *        if an error occurs, in which case global variable errno is
  526.  *        set to one of:
  527.  *
  528.  *            ENOMEM                    Failed to allocate memory for the socket
  529.  *                              data structures.
  530.  *
  531.  *            ESOCKTNOSUPPORT     Type wasn't SOCK_STREAM or SOCK_DGRAM.
  532.  *
  533.  *            EMFILE              The socket descriptor table is full.
  534.  */
  535.  
  536. Socket * INETSocketDomain::socket(int type, short)
  537. {
  538.     INETSocket * sock    =    nil;
  539.     
  540.     errno    =    0;
  541.  
  542.     if (!Driver())
  543.         return (Socket *) GUSI_error_nil(ENETDOWN);
  544.         
  545.     switch (type)    {
  546.     case SOCK_STREAM:
  547.         sock = new TCPSocket();
  548.         break;
  549.     case SOCK_DGRAM:
  550.         sock = new UDPSocket();
  551.         break;
  552.     default:
  553.         GUSI_error(ESOCKTNOSUPPORT);
  554.     }    
  555.     
  556.     if (sock && errno)    {
  557.         delete sock;
  558.         
  559.         return nil;
  560.     } 
  561.         
  562.     return sock;
  563. }
  564.  
  565. AnnotatedPB * INETSocketDomain::GetPB()
  566. {
  567.     AnnotatedPB    *    curPB;
  568.     
  569.     do {
  570.         if (++pbLast == NUM_PBS)
  571.             pbLast = 0;
  572.         
  573.         curPB = pbList + pbLast;
  574.     } while (curPB->Busy());
  575.     
  576.     return curPB;
  577. }
  578.  
  579. void INETSocketDomain::OpenSocket()
  580. {
  581.     if (!inetCount++) 
  582.         if (!(pbList = new AnnotatedPB[NUM_PBS])) {
  583.             errno             =     ENOMEM;
  584.             inetCount    =    0;
  585.         }
  586. }
  587.  
  588. void INETSocketDomain::CloseSocket()
  589. {
  590.     if (!--inetCount) {
  591.         delete [] pbList;
  592.         
  593.         pbList = nil;
  594.     }
  595. }
  596.