home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tcltk805.zip / tcl805s.zip / tcl8.0.5 / os2 / tclOS2Sock.c < prev    next >
C/C++ Source or Header  |  2001-02-09  |  36KB  |  1,146 lines

  1. /* 
  2.  * tclOS2Sock.c --
  3.  *
  4.  *    This file contains OS/2-specific socket related code.
  5.  *
  6.  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
  7.  * Copyright (c) 1996-2001 Illya Vaes
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  */
  13.  
  14. #include "tclOS2Int.h"
  15.  
  16. /*
  17.  * There is no portable macro for the maximum length
  18.  * of host names returned by gethostbyname().  We should only
  19.  * trust SYS_NMLN if it is at least 255 + 1 bytes to comply with DNS
  20.  * host name limits.
  21.  *
  22.  * Note:  SYS_NMLN is a restriction on "uname" not on gethostbyname!
  23.  *
  24.  * For example HP-UX 10.20 has SYS_NMLN == 9,  while gethostbyname()
  25.  * can return a fully qualified name from DNS of up to 255 bytes.
  26.  *
  27.  * Fix suggested by Viktor Dukhovni (viktor@esm.com)
  28.  */
  29.  
  30. #if defined(SYS_NMLN) && SYS_NMLEN >= 256
  31. #define TCL_HOSTNAME_LEN SYS_NMLEN
  32. #else
  33. #define TCL_HOSTNAME_LEN 256
  34. #endif
  35.  
  36. /*
  37.  * The following variable holds the network name of this host.
  38.  */
  39.  
  40. static char hostname[TCL_HOSTNAME_LEN + 1];
  41. static int  hostnameInited = 0;
  42.  
  43.  
  44. /*
  45.  * This structure describes per-instance state of a tcp based channel.
  46.  */
  47.  
  48. typedef struct TcpState {
  49.     Tcl_Channel channel;        /* Channel associated with this file. */
  50.     int fd;                     /* The socket itself. */
  51.     int flags;                  /* ORed combination of the bitfields
  52.                                  * defined below. */
  53.     Tcl_TcpAcceptProc *acceptProc;
  54.                                 /* Proc to call on accept. */
  55.     ClientData acceptProcData;  /* The data for the accept proc. */
  56. } TcpState;
  57.  
  58. /*
  59.  * These bits may be ORed together into the "flags" field of a TcpState
  60.  * structure.
  61.  */
  62.  
  63. #define TCP_ASYNC_SOCKET        (1<<0)  /* Asynchronous socket. */
  64. #define TCP_ASYNC_CONNECT       (1<<1)  /* Async connect in progress. */
  65.  
  66. /*
  67.  * The following defines the maximum length of the listen queue. This is
  68.  * the number of outstanding yet-to-be-serviced requests for a connection
  69.  * on a server socket, more than this number of outstanding requests and
  70.  * the connection request will fail.
  71.  */
  72.  
  73. #ifndef SOMAXCONN
  74. #define SOMAXCONN       100
  75. #endif
  76.  
  77. #if     (SOMAXCONN < 100)
  78. #undef  SOMAXCONN
  79. #define SOMAXCONN       100
  80. #endif
  81.  
  82. /*
  83.  * The following defines how much buffer space the kernel should maintain
  84.  * for a socket.
  85.  */
  86.  
  87. #define SOCKET_BUFSIZE  4096
  88.  
  89. /*
  90.  * Static routines for this file:
  91.  */
  92.  
  93. static TcpState *       CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
  94.                             int port, char *host, int server,
  95.                             char *myaddr, int myport, int async));
  96. static int              CreateSocketAddress _ANSI_ARGS_(
  97.                             (struct sockaddr_in *sockaddrPtr,
  98.                             char *host, int port));
  99. static void             TcpAccept _ANSI_ARGS_((ClientData data, int mask));
  100. static int              TcpBlockModeProc _ANSI_ARGS_((ClientData data,
  101.                             int mode));
  102. static int              TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
  103.                             Tcl_Interp *interp));
  104. static int              TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  105.                             int direction, ClientData *handlePtr));
  106. static int              TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  107.                             Tcl_Interp *interp, char *optionName,
  108.                             Tcl_DString *dsPtr));
  109. static int              TcpInputProc _ANSI_ARGS_((ClientData instanceData,
  110.                             char *buf, int toRead,  int *errorCode));
  111. static int              TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
  112.                             char *buf, int toWrite, int *errorCode));
  113. static void             TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
  114.                             int mask));
  115. static int              WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
  116.                             int *errorCodePtr));
  117.  
  118. /*
  119.  * This structure describes the channel type structure for TCP socket
  120.  * based IO:
  121.  */
  122.  
  123. static Tcl_ChannelType tcpChannelType = {
  124.     "tcp",                              /* Type name. */
  125.     TcpBlockModeProc,                   /* Set blocking/nonblocking mode.*/
  126.     TcpCloseProc,                       /* Close proc. */
  127.     TcpInputProc,                       /* Input proc. */
  128.     TcpOutputProc,                      /* Output proc. */
  129.     NULL,                               /* Seek proc. */
  130.     NULL,                               /* Set option proc. */
  131.     TcpGetOptionProc,                   /* Get option proc. */
  132.     TcpWatchProc,                       /* Initialize notifier. */
  133.     TcpGetHandleProc,                   /* Get OS handles out of channel. */
  134. };
  135.  
  136. /*
  137.  *----------------------------------------------------------------------
  138.  *
  139.  * Tcl_GetHostName --
  140.  *
  141.  *    Returns the name of the local host.
  142.  *
  143.  * Results:
  144.  *      A string containing the network name for this machine, or
  145.  *      an empty string if we can't figure out the name.  The caller
  146.  *      must not modify or free this string.
  147.  *
  148.  * Side effects:
  149.  *    None.
  150.  *
  151.  *----------------------------------------------------------------------
  152.  */
  153.  
  154. char *
  155. Tcl_GetHostName()
  156. {
  157.     struct utsname u;
  158.     struct hostent *hp;
  159.  
  160. #ifdef VERBOSE
  161.     printf("Tcl_GetHostName\n");
  162. #endif
  163.  
  164.     if (hostnameInited) {
  165.         return hostname;
  166.     }
  167.  
  168.     (VOID *) memset((VOID *) &u, (int) 0, sizeof(struct utsname));
  169.     if (uname(&u) > -1) {
  170.         hp = gethostbyname(u.nodename);
  171.         if (hp != NULL) {
  172.             strcpy(hostname, hp->h_name);
  173.         } else {
  174.             strcpy(hostname, u.nodename);
  175.         }
  176.         hostnameInited = 1;
  177.         return hostname;
  178.     }
  179.  
  180.     hostname[0] = 0;
  181.     return hostname;
  182. }
  183.  
  184. /*
  185.  *----------------------------------------------------------------------
  186.  *
  187.  * TcpBlockModeProc --
  188.  *
  189.  *      This procedure is invoked by the generic IO level to set blocking
  190.  *      and nonblocking mode on a TCP socket based channel.
  191.  *
  192.  * Results:
  193.  *      0 if successful, errno when failed.
  194.  *
  195.  * Side effects:
  196.  *      Sets the device into blocking or nonblocking mode.
  197.  *
  198.  *----------------------------------------------------------------------
  199.  */
  200.  
  201.         /* ARGSUSED */
  202. static int
  203. TcpBlockModeProc(instanceData, mode)
  204.     ClientData instanceData;            /* Socket state. */
  205.     int mode;                           /* The mode to set. Can be one of
  206.                                          * TCL_MODE_BLOCKING or
  207.                                          * TCL_MODE_NONBLOCKING. */
  208. {
  209.     TcpState *statePtr = (TcpState *) instanceData;
  210.     int setting;
  211.  
  212. #ifdef VERBOSE
  213.     printf("TcpBlockModeProc\n");
  214.     fflush(stdout);
  215. #endif
  216. #ifndef USE_FIONBIO
  217.     setting = fcntl(statePtr->fd, F_GETFL);
  218.     if (mode == TCL_MODE_BLOCKING) {
  219.         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
  220.         setting &= (~(O_NONBLOCK));
  221.     } else {
  222.         statePtr->flags |= TCP_ASYNC_SOCKET;
  223.         setting |= O_NONBLOCK;
  224.     }
  225.     if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
  226.         return errno;
  227.     }
  228. #endif
  229.  
  230. #ifdef  USE_FIONBIO
  231.     if (mode == TCL_MODE_BLOCKING) {
  232.         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
  233.         setting = 0;
  234.         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
  235.             return errno;
  236.         }
  237.     } else {
  238.         statePtr->flags |= TCP_ASYNC_SOCKET;
  239.         setting = 1;
  240.         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
  241.             return errno;
  242.         }
  243.     }
  244. #endif
  245.  
  246.     return 0;
  247. }
  248.  
  249. /*
  250.  *----------------------------------------------------------------------
  251.  *
  252.  * WaitForConnect --
  253.  *
  254.  *      Waits for a connection on an asynchronously opened socket to
  255.  *      be completed.
  256.  *
  257.  * Results:
  258.  *      None.
  259.  *
  260.  * Side effects:
  261.  *      The socket is connected after this function returns.
  262.  *
  263.  *----------------------------------------------------------------------
  264.  */
  265.  
  266. static int
  267. WaitForConnect(statePtr, errorCodePtr)
  268.     TcpState *statePtr;         /* State of the socket. */
  269.     int *errorCodePtr;          /* Where to store errors? */
  270. {
  271.     int timeOut;                /* How long to wait. */
  272.     int state;                  /* Of calling TclWaitForFile. */
  273.     int flags;                  /* fcntl flags for the socket. */
  274.  
  275. #ifdef VERBOSE
  276.     printf("WaitForConnect\n");
  277.     fflush(stdout);
  278. #endif
  279.     /*
  280.      * If an asynchronous connect is in progress, attempt to wait for it
  281.      * to complete before reading.
  282.      */
  283.  
  284.     if (statePtr->flags & TCP_ASYNC_CONNECT) {
  285.         if (statePtr->flags & TCP_ASYNC_SOCKET) {
  286.             timeOut = 0;
  287.         } else {
  288.             timeOut = -1;
  289.         }
  290.         errno = 0;
  291.         state = TclOS2WaitForFile(statePtr->fd, TCL_WRITABLE | TCL_EXCEPTION,
  292.                                   timeOut);
  293.         if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
  294. #ifndef USE_FIONBIO
  295.             flags = fcntl(statePtr->fd, F_GETFL);
  296.             flags &= (~(O_NONBLOCK));
  297.             (void) fcntl(statePtr->fd, F_SETFL, flags);
  298. #endif
  299.  
  300. #ifdef  USE_FIONBIO
  301.             flags = 0;
  302.             (void) ioctl(statePtr->fd, FIONBIO, &flags);
  303. #endif
  304.         }
  305.         if (state & TCL_EXCEPTION) {
  306.             return -1;
  307.         }
  308.         if (state & TCL_WRITABLE) {
  309.             statePtr->flags &= (~(TCP_ASYNC_CONNECT));
  310.         } else if (timeOut == 0) {
  311.             *errorCodePtr = errno = EWOULDBLOCK;
  312.             return -1;
  313.         }
  314.     }
  315.     return 0;
  316. }
  317.  
  318. /*
  319.  *----------------------------------------------------------------------
  320.  *
  321.  * TcpInputProc --
  322.  *
  323.  *      This procedure is invoked by the generic IO level to read input
  324.  *      from a TCP socket based channel.
  325.  *
  326.  *      NOTE: We cannot share code with FilePipeInputProc because here
  327.  *      we must use recv to obtain the input from the channel, not read.
  328.  *
  329.  * Results:
  330.  *      The number of bytes read is returned or -1 on error. An output
  331.  *      argument contains the POSIX error code on error, or zero if no
  332.  *      error occurred.
  333.  *
  334.  * Side effects:
  335.  *      Reads input from the input device of the channel.
  336.  *
  337.  *----------------------------------------------------------------------
  338.  */
  339.  
  340.         /* ARGSUSED */
  341. static int
  342. TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
  343.     ClientData instanceData;            /* Socket state. */
  344.     char *buf;                          /* Where to store data read. */
  345.     int bufSize;                        /* How much space is available
  346.                                          * in the buffer? */
  347.     int *errorCodePtr;                  /* Where to store error code. */
  348. {
  349.     TcpState *statePtr = (TcpState *) instanceData;
  350.     int bytesRead, state;
  351.  
  352. #ifdef VERBOSE
  353.     printf("TcpInputProc\n");
  354.     fflush(stdout);
  355. #endif
  356.     *errorCodePtr = 0;
  357.     state = WaitForConnect(statePtr, errorCodePtr);
  358. #ifdef VERBOSE
  359.     printf("    state %d\n", state);
  360.     fflush(stdout);
  361. #endif
  362.     if (state != 0) {
  363.         return -1;
  364.     }
  365.     bytesRead = recv(statePtr->fd, buf, bufSize, 0);
  366.     if (bytesRead > -1) {
  367.         return bytesRead;
  368.     }
  369.     if (errno == ECONNRESET) {
  370.  
  371.         /*
  372.          * Turn ECONNRESET into a soft EOF condition.
  373.          */
  374.  
  375.         return 0;
  376.     }
  377.     *errorCodePtr = errno;
  378.     return -1;
  379. }
  380.  
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * TcpOutputProc --
  385.  *
  386.  *      This procedure is invoked by the generic IO level to write output
  387.  *      to a TCP socket based channel.
  388.  *
  389.  *      NOTE: We cannot share code with FilePipeOutputProc because here
  390.  *      we must use send, not write, to get reliable error reporting.
  391.  *
  392.  * Results:
  393.  *      The number of bytes written is returned. An output argument is
  394.  *      set to a POSIX error code if an error occurred, or zero.
  395.  *
  396.  * Side effects:
  397.  *      Writes output on the output device of the channel.
  398.  *
  399.  *----------------------------------------------------------------------
  400.  */
  401.  
  402. static int
  403. TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
  404.     ClientData instanceData;            /* Socket state. */
  405.     char *buf;                          /* The data buffer. */
  406.     int toWrite;                        /* How many bytes to write? */
  407.     int *errorCodePtr;                  /* Where to store error code. */
  408. {
  409.     TcpState *statePtr = (TcpState *) instanceData;
  410.     int written;
  411.     int state;                          /* Of waiting for connection. */
  412.  
  413. #ifdef VERBOSE
  414.     printf("TcpOutputProc [%s]\n", buf);
  415.     fflush(stdout);
  416. #endif
  417.     *errorCodePtr = 0;
  418.     state = WaitForConnect(statePtr, errorCodePtr);
  419.     if (state != 0) {
  420.         return -1;
  421.     }
  422.     written = send(statePtr->fd, buf, toWrite, 0);
  423.     if (written > -1) {
  424.         return written;
  425.     }
  426.     *errorCodePtr = errno;
  427.     return -1;
  428. }
  429.  
  430. /*
  431.  *----------------------------------------------------------------------
  432.  *
  433.  * TcpCloseProc --
  434.  *
  435.  *      This procedure is invoked by the generic IO level to perform
  436.  *      channel-type-specific cleanup when a TCP socket based channel
  437.  *      is closed.
  438.  *
  439.  * Results:
  440.  *      0 if successful, the value of errno if failed.
  441.  *
  442.  * Side effects:
  443.  *      Closes the socket of the channel.
  444.  *
  445.  *----------------------------------------------------------------------
  446.  */
  447.  
  448.         /* ARGSUSED */
  449. static int
  450. TcpCloseProc(instanceData, interp)
  451.     ClientData instanceData;    /* The socket to close. */
  452.     Tcl_Interp *interp;         /* For error reporting - unused. */
  453. {
  454.     TcpState *statePtr = (TcpState *) instanceData;
  455.     int errorCode = 0;
  456.  
  457. #ifdef VERBOSE
  458.     printf("TcpCloseProc\n");
  459.     fflush(stdout);
  460. #endif
  461.  
  462.     /*
  463.      * Delete a file handler that may be active for this socket if this
  464.      * is a server socket - the file handler was created automatically
  465.      * by Tcl as part of the mechanism to accept new client connections.
  466.      * Channel handlers are already deleted in the generic IO channel
  467.      * closing code that called this function, so we do not have to
  468.      * delete them here.
  469.      */
  470.  
  471.     Tcl_DeleteFileHandler(statePtr->fd);
  472.  
  473.     if (close(statePtr->fd) < 0) {
  474.         errorCode = errno;
  475.     }
  476.     ckfree((char *) statePtr);
  477.  
  478.     return errorCode;
  479. }
  480.  
  481. /*
  482.  *----------------------------------------------------------------------
  483.  *
  484.  * TcpGetOptionProc --
  485.  *
  486.  *      Computes an option value for a TCP socket based channel, or a
  487.  *      list of all options and their values.
  488.  *
  489.  *      Note: This code is based on code contributed by John Haxby.
  490.  *
  491.  * Results:
  492.  *      A standard Tcl result. The value of the specified option or a
  493.  *      list of all options and their values is returned in the
  494.  *      supplied DString.
  495.  *
  496.  * Side effects:
  497.  *      None.
  498.  *
  499.  *----------------------------------------------------------------------
  500.  */
  501.  
  502. static int
  503. TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
  504.     ClientData instanceData;     /* Socket state. */
  505.     Tcl_Interp *interp;          /* For error reporting - can be NULL. */
  506.     char *optionName;            /* Name of the option to
  507.                                   * retrieve the value for, or
  508.                                   * NULL to get all options and
  509.                                   * their values. */
  510.     Tcl_DString *dsPtr;          /* Where to store the computed
  511.                                   * value; initialized by caller. */
  512. {
  513.     TcpState *statePtr = (TcpState *) instanceData;
  514.     struct sockaddr_in sockname;
  515.     struct sockaddr_in peername;
  516.     struct hostent *hostEntPtr;
  517.     int size = sizeof(struct sockaddr_in);
  518.     size_t len = 0;
  519.     char buf[128];
  520.  
  521. #ifdef VERBOSE
  522.     printf("TcpGetOptionProc\n");
  523.     fflush(stdout);
  524. #endif
  525.     if (optionName != (char *) NULL) {
  526.         len = strlen(optionName);
  527.     }
  528.  
  529.     if ((len == 0) ||
  530.             ((len > 1) && (optionName[1] == 'p') &&
  531.                     (strncmp(optionName, "-peername", len) == 0))) {
  532.         if (getpeername(statePtr->fd, (struct sockaddr *) &peername, &size)
  533.                 >= 0) {
  534.             if (len == 0) {
  535.                 Tcl_DStringAppendElement(dsPtr, "-peername");
  536.                 Tcl_DStringStartSublist(dsPtr);
  537.             }
  538.             Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
  539.             hostEntPtr = gethostbyaddr((char *) &(peername.sin_addr),
  540.                     sizeof(peername.sin_addr), AF_INET);
  541.             if (hostEntPtr != (struct hostent *) NULL) {
  542.                 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
  543.             } else {
  544.                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
  545.             }
  546.             sprintf(buf, "%d", ntohs(peername.sin_port));
  547.             Tcl_DStringAppendElement(dsPtr, buf);
  548.             if (len == 0) {
  549.                 Tcl_DStringEndSublist(dsPtr);
  550.             } else {
  551.                 return TCL_OK;
  552.             }
  553.         } else {
  554.             /*
  555.              * getpeername failed - but if we were asked for all the options
  556.              * (len==0), don't flag an error at that point because it could
  557.              * be an fconfigure request on a server socket. (which have
  558.              * no peer). same must be done on win&mac.
  559.              */
  560.  
  561.             if (len) {
  562.                 if (interp) {
  563.                     Tcl_AppendResult(interp, "can't get peername: ",
  564.                                      Tcl_PosixError(interp),
  565.                                      (char *) NULL);
  566.                 }
  567.                 return TCL_ERROR;
  568.             }
  569.         }
  570.     }
  571.  
  572.     if ((len == 0) ||
  573.             ((len > 1) && (optionName[1] == 's') &&
  574.                     (strncmp(optionName, "-sockname", len) == 0))) {
  575.         if (getsockname(statePtr->fd, (struct sockaddr *) &sockname, &size)
  576.                 >= 0) {
  577.             if (len == 0) {
  578.                 Tcl_DStringAppendElement(dsPtr, "-sockname");
  579.                 Tcl_DStringStartSublist(dsPtr);
  580.             }
  581.             Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
  582.             hostEntPtr = gethostbyaddr((char *) &(sockname.sin_addr),
  583.                     sizeof(peername.sin_addr), AF_INET);
  584.             if (hostEntPtr != (struct hostent *) NULL) {
  585.                 Tcl_DStringAppendElement(dsPtr, (char *) hostEntPtr->h_name);
  586.             } else {
  587.                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
  588.             }
  589.             sprintf(buf, "%d", ntohs(sockname.sin_port));
  590.             Tcl_DStringAppendElement(dsPtr, buf);
  591.             if (len == 0) {
  592.                 Tcl_DStringEndSublist(dsPtr);
  593.             } else {
  594.                 return TCL_OK;
  595.             }
  596.         } else {
  597.             if (interp) {
  598.                 Tcl_AppendResult(interp, "can't get sockname: ",
  599.                                  Tcl_PosixError(interp),
  600.                                  (char *) NULL);
  601.             }
  602.             return TCL_ERROR;
  603.         }
  604.     }
  605.  
  606.     if (len > 0) {
  607.         return Tcl_BadChannelOption(interp, optionName, "peername sockname");
  608.     }
  609.  
  610.     return TCL_OK;
  611. }
  612.  
  613. /*
  614.  *----------------------------------------------------------------------
  615.  *
  616.  * TcpWatchProc --
  617.  *
  618.  *      Initialize the notifier to watch Tcl_Files from this channel.
  619.  *
  620.  * Results:
  621.  *      None.
  622.  *
  623.  * Side effects:
  624.  *      Sets up the notifier so that a future event on the channel will
  625.  *      be seen by Tcl.
  626.  *
  627.  *----------------------------------------------------------------------
  628.  */
  629.  
  630. static void
  631. TcpWatchProc(instanceData, mask)
  632.     ClientData instanceData;            /* The socket state. */
  633.     int mask;                           /* Events of interest; an OR-ed
  634.                                          * combination of TCL_READABLE,
  635.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  636. {
  637.     TcpState *statePtr = (TcpState *) instanceData;
  638.  
  639.     if (mask) {
  640.         Tcl_CreateFileHandler(statePtr->fd, mask,
  641.                 (Tcl_FileProc *) Tcl_NotifyChannel,
  642.                 (ClientData) statePtr->channel);
  643.     } else {
  644.         Tcl_DeleteFileHandler(statePtr->fd);
  645.     }
  646. }
  647.  
  648. /*
  649.  *----------------------------------------------------------------------
  650.  *
  651.  * TcpGetHandleProc --
  652.  *
  653.  *      Called from Tcl_GetChannelFile to retrieve OS handles from inside
  654.  *      a TCP socket based channel.
  655.  *
  656.  * Results:
  657.  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  658.  *      there is no handle for the specified direction.
  659.  *
  660.  * Side effects:
  661.  *      None.
  662.  *
  663.  *----------------------------------------------------------------------
  664.  */
  665.  
  666.         /* ARGSUSED */
  667. static int
  668. TcpGetHandleProc(instanceData, direction, handlePtr)
  669.     ClientData instanceData;    /* The socket state. */
  670.     int direction;              /* Not used. */
  671.     ClientData *handlePtr;      /* Where to store the handle.  */
  672. {
  673.     TcpState *statePtr = (TcpState *) instanceData;
  674.  
  675.     *handlePtr = (ClientData)statePtr->fd;
  676.     return TCL_OK;
  677. }
  678.  
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * CreateSocket --
  683.  *
  684.  *      This function opens a new socket in client or server mode
  685.  *      and initializes the TcpState structure.
  686.  *
  687.  * Results:
  688.  *      Returns a new TcpState, or NULL with an error in interp->result,
  689.  *      if interp is not NULL.
  690.  *
  691.  * Side effects:
  692.  *      Opens a socket.
  693.  *
  694.  *----------------------------------------------------------------------
  695.  */
  696.  
  697. static TcpState *
  698. CreateSocket(interp, port, host, server, myaddr, myport, async)
  699.     Tcl_Interp *interp;         /* For error reporting; can be NULL. */
  700.     int port;                   /* Port number to open. */
  701.     char *host;                 /* Name of host on which to open port.
  702.                                  * NULL implies INADDR_ANY */
  703.     int server;                 /* 1 if socket should be a server socket,
  704.                                  * else 0 for a client socket. */
  705.     char *myaddr;               /* Optional client-side address */
  706.     int myport;                 /* Optional client-side port */
  707.     int async;                  /* If nonzero and creating a client socket,
  708.                                  * attempt to do an async connect. Otherwise
  709.                                  * do a synchronous connect or bind. */
  710. {
  711.     int status, sock, asyncConnect, curState, origState;
  712.     struct sockaddr_in sockaddr;        /* socket address */
  713.     struct sockaddr_in mysockaddr;      /* Socket address for client */
  714.     TcpState *statePtr;
  715.  
  716. #ifdef VERBOSE
  717.     printf("CreateSocket %d %s s?%d\n", port, host, server);
  718.     fflush(stdout);
  719. #endif
  720.     sock = -1;
  721.     origState = 0;
  722.     if (! CreateSocketAddress(&sockaddr, host, port)) {
  723.         goto addressError;
  724.     }
  725.     if ((myaddr != NULL || myport != 0) &&
  726.             ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
  727.         goto addressError;
  728.     }
  729.  
  730.     sock = socket(AF_INET, SOCK_STREAM, 0);
  731.     if (sock < 0) {
  732.         goto addressError;
  733.     }
  734.  
  735.     /*
  736.      * Set the close-on-exec flag so that the socket will not get
  737.      * inherited by child processes.
  738.      */
  739.  
  740.     fcntl(sock, F_SETFD, FD_CLOEXEC);
  741.  
  742.     /*
  743.      * Set kernel space buffering
  744.      */
  745.  
  746.     TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
  747.  
  748.     asyncConnect = 0;
  749.     status = 0;
  750.     if (server) {
  751.  
  752.         /*
  753.          * Set up to reuse server addresses automatically and bind to the
  754.          * specified port.
  755.          */
  756.  
  757.         status = 1;
  758.         (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
  759.                 sizeof(status));
  760.         status = bind(sock, (struct sockaddr *) &sockaddr,
  761.                 sizeof(struct sockaddr));
  762.         if (status != -1) {
  763.             status = listen(sock, SOMAXCONN);
  764.         }
  765.     } else {
  766.         if (myaddr != NULL || myport != 0) {
  767.             status = 1;
  768.             (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
  769.                     (char *) &curState, sizeof(status));
  770.             status = bind(sock, (struct sockaddr *) &mysockaddr,
  771.                     sizeof(struct sockaddr));
  772.             if (status < 0) {
  773.                 goto bindError;
  774.             }
  775.         }
  776.  
  777.         /*
  778.          * Attempt to connect. The connect may fail at present with an
  779.          * EINPROGRESS but at a later time it will complete. The caller
  780.          * will set up a file handler on the socket if she is interested in
  781.          * being informed when the connect completes.
  782.          */
  783.  
  784.         if (async) {
  785. #ifndef USE_FIONBIO
  786.             origState = fcntl(sock, F_GETFL);
  787.             curState = origState | O_NONBLOCK;
  788.             status = fcntl(sock, F_SETFL, curState);
  789. #endif
  790.  
  791. #ifdef  USE_FIONBIO
  792.             curState = 1;
  793.             status = ioctl(sock, FIONBIO, &curState);
  794. #endif
  795.         } else {
  796.             status = 0;
  797.         }
  798.         if (status > -1) {
  799.             status = connect(sock, (struct sockaddr *) &sockaddr,
  800.                     sizeof(sockaddr));
  801.             if (status < 0) {
  802.                 if (errno == EINPROGRESS) {
  803.                     asyncConnect = 1;
  804.                     status = 0;
  805.                 }
  806.             }
  807.         }
  808.     }
  809.  
  810. bindError:
  811.     if (status < 0) {
  812.         if (interp != NULL) {
  813.             Tcl_AppendResult(interp, "couldn't open socket: ",
  814.                     Tcl_PosixError(interp), (char *) NULL);
  815.         }
  816.         if (sock != -1) {
  817.             close(sock);
  818.         }
  819.         return NULL;
  820.     }
  821.  
  822.     /*
  823.      * Allocate a new TcpState for this socket.
  824.      */
  825.  
  826.     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  827.     statePtr->flags = 0;
  828.     if (asyncConnect) {
  829.         statePtr->flags = TCP_ASYNC_CONNECT;
  830.     }
  831.     statePtr->fd = sock;
  832.  
  833.     return statePtr;
  834.  
  835. addressError:
  836.     if (sock != -1) {
  837.         close(sock);
  838.     }
  839.     if (interp != NULL) {
  840.         Tcl_AppendResult(interp, "couldn't open socket: ",
  841.                          Tcl_PosixError(interp), (char *) NULL);
  842.     }
  843.     return NULL;
  844. }
  845.  
  846. /*
  847.  *----------------------------------------------------------------------
  848.  *
  849.  * CreateSocketAddress --
  850.  *
  851.  *      This function initializes a sockaddr structure for a host and port.
  852.  *
  853.  * Results:
  854.  *      1 if the host was valid, 0 if the host could not be converted to
  855.  *      an IP address.
  856.  *
  857.  * Side effects:
  858.  *      Fills in the *sockaddrPtr structure.
  859.  *
  860.  *----------------------------------------------------------------------
  861.  */
  862.  
  863. static int
  864. CreateSocketAddress(sockaddrPtr, host, port)
  865.     struct sockaddr_in *sockaddrPtr;    /* Socket address */
  866.     char *host;                         /* Host.  NULL implies INADDR_ANY */
  867.     int port;                           /* Port number */
  868. {
  869.     struct hostent *hostent;            /* Host database entry */
  870.     struct in_addr addr;                /* For 64/32 bit madness */
  871.  
  872. #ifdef VERBOSE
  873.     printf("CreateSocketAddress\n");
  874.     fflush(stdout);
  875. #endif
  876.     (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
  877.     sockaddrPtr->sin_family = AF_INET;
  878.     sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
  879.     if (host == NULL) {
  880.         addr.s_addr = INADDR_ANY;
  881.     } else {
  882.         addr.s_addr = inet_addr(host);
  883.         if (addr.s_addr == (unsigned long) -1) {
  884.             hostent = gethostbyname(host);
  885.             if (hostent != NULL) {
  886.                 memcpy((VOID *) &addr,
  887.                         (VOID *) hostent->h_addr_list[0],
  888.                         (size_t) hostent->h_length);
  889.             } else {
  890. #ifdef  EHOSTUNREACH
  891.                 errno = EHOSTUNREACH;
  892. #else
  893. #ifdef ENXIO
  894.                 errno = ENXIO;
  895. #endif
  896. #endif
  897.                 return 0;       /* error */
  898.             }
  899.         }
  900.     }
  901.  
  902.     /*
  903.      * NOTE: On 64 bit machines the assignment below is rumored to not
  904.      * do the right thing. Please report errors related to this if you
  905.      * observe incorrect behavior on 64 bit machines such as DEC Alphas.
  906.      * Should we modify this code to do an explicit memcpy?
  907.      */
  908.  
  909.     sockaddrPtr->sin_addr.s_addr = addr.s_addr;
  910.     return 1;   /* Success. */
  911. }
  912.  
  913. /*
  914.  *----------------------------------------------------------------------
  915.  *
  916.  * Tcl_OpenTcpClient --
  917.  *
  918.  *      Opens a TCP client socket and creates a channel around it.
  919.  *
  920.  * Results:
  921.  *      The channel or NULL if failed.  An error message is returned
  922.  *      in the interpreter on failure.
  923.  *
  924.  * Side effects:
  925.  *      Opens a client socket and creates a new channel.
  926.  *
  927.  *----------------------------------------------------------------------
  928.  */
  929.  
  930. Tcl_Channel
  931. Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
  932.     Tcl_Interp *interp;                 /* For error reporting; can be NULL. */
  933.     int port;                           /* Port number to open. */
  934.     char *host;                         /* Host on which to open port. */
  935.     char *myaddr;                       /* Client-side address */
  936.     int myport;                         /* Client-side port */
  937.     int async;                          /* If nonzero, attempt to do an
  938.                                          * asynchronous connect. Otherwise
  939.                                          * we do a blocking connect. */
  940. {
  941.     TcpState *statePtr;
  942.     char channelName[20];
  943.  
  944. #ifdef VERBOSE
  945.     printf("Tcl_OpenTcpClient\n");
  946.     fflush(stdout);
  947. #endif
  948.     /*
  949.      * Create a new client socket and wrap it in a channel.
  950.      */
  951.  
  952.     statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
  953.     if (statePtr == NULL) {
  954.         return NULL;
  955.     }
  956.  
  957.     statePtr->acceptProc = NULL;
  958.     statePtr->acceptProcData = (ClientData) NULL;
  959.  
  960.     sprintf(channelName, "sock%d", statePtr->fd);
  961.  
  962.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  963.                                           (ClientData) statePtr,
  964.                                           (TCL_READABLE | TCL_WRITABLE));
  965.     if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
  966.                              "auto crlf") == TCL_ERROR) {
  967.         Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
  968.         return NULL;
  969.     }
  970.     return statePtr->channel;
  971. }
  972.  
  973. /*
  974.  *----------------------------------------------------------------------
  975.  *
  976.  * Tcl_MakeTcpClientChannel --
  977.  *
  978.  *      Creates a Tcl_Channel from an existing client TCP socket.
  979.  *
  980.  * Results:
  981.  *      The Tcl_Channel wrapped around the preexisting TCP socket.
  982.  *
  983.  * Side effects:
  984.  *      None.
  985.  *
  986.  *----------------------------------------------------------------------
  987.  */
  988.  
  989. Tcl_Channel
  990. Tcl_MakeTcpClientChannel(sock)
  991.     ClientData sock;            /* The socket to wrap up into a channel. */
  992. {
  993.     TcpState *statePtr;
  994.     char channelName[20];
  995.  
  996. #ifdef VERBOSE
  997.     printf("Tcl_MakeTcpClientChannel\n");
  998.     fflush(stdout);
  999. #endif
  1000.     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  1001.     statePtr->fd = (int) sock;
  1002.     statePtr->acceptProc = NULL;
  1003.     statePtr->acceptProcData = (ClientData) NULL;
  1004.  
  1005.     sprintf(channelName, "sock%d", statePtr->fd);
  1006.  
  1007.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1008.                                           (ClientData) statePtr,
  1009.                                           (TCL_READABLE | TCL_WRITABLE));
  1010.     if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel,
  1011.                              "-translation", "auto crlf") == TCL_ERROR) {
  1012.         Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
  1013.         return NULL;
  1014.     }
  1015.     return statePtr->channel;
  1016. }
  1017.  
  1018. /*
  1019.  *----------------------------------------------------------------------
  1020.  *
  1021.  * Tcl_OpenTcpServer --
  1022.  *
  1023.  *      Opens a TCP server socket and creates a channel around it.
  1024.  *
  1025.  * Results:
  1026.  *      The channel or NULL if failed. If an error occurred, an
  1027.  *      error message is left in interp->result if interp is
  1028.  *      not NULL.
  1029.  *
  1030.  * Side effects:
  1031.  *      Opens a server socket and creates a new channel.
  1032.  *
  1033.  *----------------------------------------------------------------------
  1034.  */
  1035.  
  1036. Tcl_Channel
  1037. Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
  1038.     Tcl_Interp *interp;                 /* For error reporting - may be
  1039.                                          * NULL. */
  1040.     int port;                           /* Port number to open. */
  1041.     char *myHost;                       /* Name of local host. */
  1042.     Tcl_TcpAcceptProc *acceptProc;      /* Callback for accepting connections
  1043.                                          * from new clients. */
  1044.     ClientData acceptProcData;          /* Data for the callback. */
  1045. {
  1046.     TcpState *statePtr;
  1047.     char channelName[20];
  1048.  
  1049. #ifdef VERBOSE
  1050.     printf("Tcl_OpenTcpServer\n");
  1051.     fflush(stdout);
  1052. #endif
  1053.     /*
  1054.      * Create a new client socket and wrap it in a channel.
  1055.      */
  1056.  
  1057.     statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
  1058.     if (statePtr == NULL) {
  1059.         return NULL;
  1060.     }
  1061.  
  1062.     statePtr->acceptProc = acceptProc;
  1063.     statePtr->acceptProcData = acceptProcData;
  1064.  
  1065.     /*
  1066.      * Set up the callback mechanism for accepting connections
  1067.      * from new clients.
  1068.      */
  1069.  
  1070.     Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
  1071.             (ClientData) statePtr);
  1072.     sprintf(channelName, "sock%d", statePtr->fd);
  1073.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1074.                                           (ClientData) statePtr, 0);
  1075.     return statePtr->channel;
  1076. }
  1077.  
  1078. /*
  1079.  *----------------------------------------------------------------------
  1080.  *
  1081.  * TcpAccept --
  1082.  *      Accept a TCP socket connection.  This is called by the event loop.
  1083.  *
  1084.  * Results:
  1085.  *      None.
  1086.  *
  1087.  * Side effects:
  1088.  *      Creates a new connection socket. Calls the registered callback
  1089.  *      for the connection acceptance mechanism.
  1090.  *
  1091.  *----------------------------------------------------------------------
  1092.  */
  1093.  
  1094.         /* ARGSUSED */
  1095. static void
  1096. TcpAccept(data, mask)
  1097.     ClientData data;                    /* Callback token. */
  1098.     int mask;                           /* Not used. */
  1099. {
  1100.     TcpState *sockState;                /* Client data of server socket. */
  1101.     int newsock;                        /* The new client socket */
  1102.     TcpState *newSockState;             /* State for new socket. */
  1103.     struct sockaddr_in addr;            /* The remote address */
  1104.     int len;                            /* For accept interface */
  1105.     char channelName[20];
  1106.  
  1107. #ifdef VERBOSE
  1108.     printf("TcpAccept\n");
  1109.     fflush(stdout);
  1110. #endif
  1111.     sockState = (TcpState *) data;
  1112.  
  1113.     len = sizeof(struct sockaddr_in);
  1114.     newsock = accept(sockState->fd, (struct sockaddr *)&addr, &len);
  1115.     if (newsock < 0) {
  1116.         return;
  1117.     }
  1118.  
  1119.     /*
  1120.      * Set close-on-exec flag to prevent the newly accepted socket from
  1121.      * being inherited by child processes.
  1122.      */
  1123.  
  1124.     (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
  1125.  
  1126.     newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  1127.  
  1128.     newSockState->flags = 0;
  1129.     newSockState->fd = newsock;
  1130.     newSockState->acceptProc = (Tcl_TcpAcceptProc *) NULL;
  1131.     newSockState->acceptProcData = (ClientData) NULL;
  1132.  
  1133.     sprintf(channelName, "sock%d", newsock);
  1134.     newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1135.             (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
  1136.  
  1137.     Tcl_SetChannelOption((Tcl_Interp *) NULL, newSockState->channel,
  1138.             "-translation", "auto crlf");
  1139.  
  1140.     if (sockState->acceptProc != (Tcl_TcpAcceptProc *) NULL) {
  1141.         (sockState->acceptProc) (sockState->acceptProcData,
  1142.                 newSockState->channel, inet_ntoa(addr.sin_addr),
  1143.                 ntohs(addr.sin_port));
  1144.     }
  1145. }
  1146.