Source File sk/cd_socket.c

    1    | /***************************************
    2    |   $Revision: 1.8 $
    3    |
    4    |   Socket module - cd_socket.c - basic read/write socket routines defined
    5    |                                 in terms of connection data structures
    6    | 				with timeouts and storing information about
    7    | 				broken connections.
    8    |
    9    |   Status: NOT REVUED, TESTED
    10   |
    11   |   Design and implementation by Marek Bukowy.
    12   |
    13   |   ******************/ /******************
    14   |   Copyright (c) 1999,2000,2001,2002                     RIPE NCC
    15   |
    16   |   All Rights Reserved
    17   |
    18   |   Permission to use, copy, modify, and distribute this software and its
    19   |   documentation for any purpose and without fee is hereby granted,
    20   |   provided that the above copyright notice appear in all copies and that
    21   |   both that copyright notice and this permission notice appear in
    22   |   supporting documentation, and that the name of the author not be
    23   |   used in advertising or publicity pertaining to distribution of the
    24   |   software without specific, written prior permission.
    25   |
    26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    32   |   ***************************************/
    33   |
    34   | #include "rip.h"
    35   |
    36   | /*+
    37   |  * -------------------------------------------------------------------
    38   |  * CD (connection data structure) varieties of the functions:
    39   |  *   broken connections get registered in the connection structure
    40   |  *   as side effects.
    41   |  * by marek
    42   |  * -----------------------------------------------------------------
    43   | +*/
    44   |
    45   | /* SK_cd_make */
    46   | /*++++++++++++++++++++++++++++++++++++++
    47   |
    48   |   Construct a connection data given the socket or file descriptor.
    49   |   Also performs the getpeername check and stores the IP in an allocated
    50   |   string.
    51   |
    52   |   sk_conn_st *condat    pointer to where the data is to be stored.
    53   |
    54   |   int  sock             The socket or file descriptor.
    55   |
    56   |   unsigned timeout      Read timeout (used in SK_cd_gets) in seconds.
    57   |                         Value of 0 disables the timeout.
    58   |   ++++++++++++++++++++++++++++++++++++++*/
    59   | void SK_cd_make(sk_conn_st *condat, int  sock, unsigned timeout)
    60   | {
    61   |   memset(condat, 0, sizeof(sk_conn_st));
    62   |
    63   |   condat->sock = sock;
    64   |
    65   |   condat->ip = SK_getpeername(sock);
    66   |   dieif(condat->ip == NULL);
    67   |
    68   |   SK_getpeerip(sock, &(condat->rIP));
    69   |   condat->eIP =  condat->rIP;
    70   |
    71   |   condat->rd_timeout.tv_sec = timeout;
    72   |
    73   |   condat->rd_buf_len = 0;
    74   | }
    75   |
    76   |
    77   | /*++++++++++++++++++++++++++++++++++++++
    78   |   Destroys the data allocated and anchored by the connection data structure.
    79   |
    80   |   sk_conn_st *condat   Pointer to the connection data structure.
    81   |
    82   |   ++++++++++++++++++++++++++++++++++++++*/
    83   | void SK_cd_free(sk_conn_st *condat)
    84   | {
    85   |   UT_free(condat->ip);
    86   | }
    87   |
    88   | /* SK_cd_puts() */
    89   | /*++++++++++++++++++++++++++++++++++++++
    90   |
    91   |    This function writes a character string out to a socket, unless
    92   |    the connection is broken.
    93   |
    94   |    int SK_cd_puts         Returns the total_count of bytes written,
    95   |                           or inverted error codes (negative numbers):
    96   | 			  (- SK_DISCONNECT) on broken connection,
    97   | 			  (- SK_INTERRUPT)  on control-c received,
    98   | 			  (- SK_TIMEOUT)    on timeout.
    99   |
    100  |    sk_conn_st *condat     Pointer to the connection data structure.
    101  |
    102  |    char   *str            The buffer to be written to the socket.
    103  |
    104  |   More:
    105  |        if the connection structure has bad status for this connection
    106  |        from previous calls, no write will be attempted.
    107  |
    108  |   +html+ <PRE>
    109  |
    110  |   Side Effects:
    111  |        broken connections get registered in the connection structure
    112  |
    113  |   +html+ </PRE>
    114  |
    115  |   ++++++++++++++++++++++++++++++++++++++*/
    116  | int SK_cd_puts(sk_conn_st *condat, const char *str)
    117  | {
    118  |   int res;
    119  |   struct timeval *ptm;
    120  |
    121  |   /* if we're not connected, return our status */
    122  |   if (condat->rtc != 0)  {
    123  |     return (-condat->rtc);
    124  |   }
    125  |
    126  |   /* bad design to use 0 to mean "infinity", but we'll keep it because
    127  |      that's the current implementation - shane */
    128  |   ptm = &condat->rd_timeout;
    129  |   if ((ptm->tv_sec == 0) && (ptm->tv_usec == 0)) { /* if timeout 0,
    130  | 						      do blocking I/O */
    131  |     ptm = NULL;
    132  |   }
    133  |
    134  |   /* use SK_puts() to do the actual work */
    135  |   res = SK_puts(condat->sock, str, ptm);
    136  |
    137  |   /* if timed out (or some other error), then set the rtc variable */
    138  |   if (res < 0) {
    139  |     condat->rtc |= SK_DISCONNECT;
    140  |     res = -SK_DISCONNECT;
    141  |   }
    142  |
    143  |   /* return documented value */
    144  |   return res;
    145  | } /* SK_cd_puts() */
    146  |
    147  | /* fill the input buffer as much as possible */
    148  | static int
    149  | sk_fill_rd_buf (sk_conn_st *condat)
    150  | {
    151  |     fd_set rset;
    152  |     struct timeval *ptm;
    153  |     int select_ret;
    154  |     int rd_buf_free;
    155  |     int read_ret;
    156  |
    157  |     ptm = &(condat->rd_timeout);
    158  |     /* if timeout is 0, do blocking I/O - bogus, but that's how it is */
    159  |     if ((ptm->tv_sec == 0) && (ptm->tv_usec == 0)) {
    160  |         ptm = NULL;
    161  |     }
    162  |     FD_ZERO(&rset);
    163  |     FD_SET(condat->sock, &rset);
    164  |
    165  |     /* wait for input to become available */
    166  |     select_ret = select(condat->sock + 1, &rset, NULL, NULL, ptm);
    167  |     if (select_ret < 0) {
    168  |         /* unfortunate, but bad things happen to good sockets - SK */
    169  |         ER_perror(FAC_SK, SK_SELECT, "(%d) %s", errno, strerror(errno));
    170  |         return -1;
    171  |     }
    172  |     if (select_ret == 0) {
    173  |         condat->rtc |= SK_TIMEOUT;
    174  |         return -1;
    175  |     }
    176  |
    177  |     /* calculate maximum amount to read */
    178  |     rd_buf_free = sizeof(condat->rd_buf) - (condat->rd_buf_len);
    179  |
    180  |     /* read up to that much, if available */
    181  |     read_ret = read(condat->sock,
    182  |                     condat->rd_buf + condat->rd_buf_len,
    183  |                     rd_buf_free);
    184  |     if (read_ret <= 0) {
    185  | 	condat->rtc |= SK_DISCONNECT;
    186  |         return -1;
    187  |     }
    188  |     condat->rd_buf_len += read_ret;
    189  |
    190  |     /* return the fact that we got data */
    191  |     return read_ret;
    192  | }
    193  |
    194  | #undef TEST_SK_FILL_RD_BUF
    195  |
    196  | /* test sk_fill_rd_buf() function - full path coverage */
    197  | #ifdef TEST_SK_FILL_RD_BUF
    198  | #include <unistd.h>
    199  | #include <stdio.h>
    200  | #include <errno.h>
    201  | #include <string.h>
    202  |
    203  | int
    204  | main ()
    205  | {
    206  |     sk_conn_st conn;
    207  |     int pfd[2];
    208  |     int tmp_fd;
    209  |     int bogus_fd;
    210  |     int i;
    211  |     int buf[INPUT_BUF_LEN+INPUT_BUF_LEN+100];
    212  |     int fill_buf_ret;
    213  |
    214  |     /* test with bogus file descriptor and 0 timeout value */
    215  |     tmp_fd = open("/dev/null", O_RDONLY);
    216  |     if (tmp_fd == -1) {
    217  |         fprintf(stderr, "Error %d creating bogus file descriptor; %s\n",
    218  |                 errno, strerror(errno));
    219  |         exit(1);
    220  |     }
    221  |     bogus_fd = tmp_fd;
    222  |     close(tmp_fd);
    223  |     SK_cd_make(&conn, bogus_fd, 0);
    224  |     if (sk_fill_rd_buf(&conn) != -1) {
    225  |         fprintf(stderr, "Filling buffer with bogus file descriptor worked\n");
    226  |         exit(1);
    227  |     }
    228  |
    229  |     /* create a pipe for further testing */
    230  |     if (pipe(pfd) != 0) {
    231  |         fprintf(stderr, "Error %d creating pipe; %s\n", errno, strerror(errno));
    232  |         exit(1);
    233  |     }
    234  |     /* real file descriptor and non-0 timeout value */
    235  |     SK_cd_make(&conn, pfd[0], 1);
    236  |
    237  |     /* test timeout */
    238  |     if (sk_fill_rd_buf(&conn) != -1) {
    239  |         fprintf(stderr, "Buffer filling failed to timeout properly\n");
    240  |         exit(1);
    241  |     }
    242  |     if (conn.rtc != SK_TIMEOUT) {
    243  |         fprintf(stderr, "Timeout didn't set flag properly\n");
    244  |         exit(1);
    245  |     }
    246  |
    247  |     /* test EOF input */
    248  |     close(pfd[1]);
    249  |     conn.rtc = 0;
    250  |     if (sk_fill_rd_buf(&conn) != -1) {
    251  |         fprintf(stderr, "Buffer filling failed to timeout properly\n");
    252  |         exit(1);
    253  |     }
    254  |     if (conn.rtc != SK_DISCONNECT) {
    255  |         fprintf(stderr, "Disconnect didn't set flag properly\n");
    256  |         exit(1);
    257  |     }
    258  |
    259  |     /* test empty buffer reading various values */
    260  |     {
    261  |         int lengths[] =
    262  |             { 1, 100, INPUT_BUF_LEN-1, INPUT_BUF_LEN,
    263  |               INPUT_BUF_LEN+1, INPUT_BUF_LEN+100 };
    264  |         for (i=0; i<sizeof(lengths)/sizeof(lengths[0]); i++) {
    265  |             close(pfd[0]);
    266  |             close(pfd[1]);
    267  |             if (pipe(pfd) != 0) {
    268  |                 fprintf(stderr, "Error %d creating pipe; %s\n",
    269  |                         errno, strerror(errno));
    270  |                 exit(1);
    271  |             }
    272  |             SK_cd_make(&conn, pfd[0], 0);
    273  |             memset(buf, i+1, lengths[i]);
    274  |             if (write(pfd[1], buf, lengths[i]) != lengths[i]) {
    275  |                 fprintf(stderr, "Error %d writing to pipe; %s\n",
    276  |                         errno, strerror(errno));
    277  |                 exit(1);
    278  |             }
    279  |             fill_buf_ret = sk_fill_rd_buf(&conn);
    280  |             if (fill_buf_ret == -1) {
    281  |                 fprintf(stderr, "Error filling buffer\n");
    282  |                 exit(1);
    283  |             }
    284  |             if (lengths[i] < INPUT_BUF_LEN) {
    285  |                 if (fill_buf_ret != lengths[i]) {
    286  |                     fprintf(stderr, "Error filling buffer\n");
    287  |                     exit(1);
    288  |                 }
    289  |                 if (memcmp(buf, conn.rd_buf, lengths[i]) != 0) {
    290  |                     fprintf(stderr, "Error with buffer contents\n");
    291  |                     exit(1);
    292  |                 }
    293  |             } else {
    294  |                 if (fill_buf_ret != INPUT_BUF_LEN) {
    295  |                     fprintf(stderr, "Error filling buffer\n");
    296  |                     exit(1);
    297  |                 }
    298  |                 if (memcmp(buf, conn.rd_buf, INPUT_BUF_LEN) != 0) {
    299  |                     fprintf(stderr, "Error with buffer contents\n");
    300  |                     exit(1);
    301  |                 }
    302  |             }
    303  |         }
    304  |     }
    305  |
    306  |     return 0;
    307  | }
    308  | #endif
    309  |
    310  | /* SK_cd_gets() */
    311  | /*++++++++++++++++++++++++++++++++++++++
    312  |
    313  |   Read from a socket, until a linefeed character is received or the buffer
    314  |   fills up to the maximum size "count". If the connection data has non-zero
    315  |   timeout value for reading, it is used here between calls to read
    316  |   the next 1 character.
    317  |
    318  |    int SK_cd_gets      Returns the total_count of bytes read,
    319  |                        or inverted error codes (negative numbers):
    320  | 		       (- SK_DISCONNECT) on broken connection,
    321  | 		       (- SK_TIMEOUT)    on timeout.
    322  |
    323  |    sk_conn_st *condat  connection data
    324  |
    325  |    char   *str         The buffer to store the data received from
    326  |                        the socket.
    327  |
    328  |    size_t count        size of the buffer.
    329  |
    330  |   More:
    331  |        if the connection structure has bad status for this connection
    332  |        from previous calls, no read will be attempted.
    333  |
    334  |   +html+ <PRE>
    335  |   Author:
    336  |         marek
    337  |
    338  |   Side Effects:
    339  |        broken connections get registered in the connection structure.
    340  |
    341  |   +html+ </PRE>
    342  |
    343  |   ++++++++++++++++++++++++++++++++++++++*/
    344  | int SK_cd_gets(sk_conn_st *condat, char *str, size_t count)
    345  | {
    346  |     char *eol;
    347  |     int line_len;
    348  |     int amt_to_copy;
    349  |
    350  |     /* leave space for terminating '\0' */
    351  |     count--;
    352  |
    353  |     /* we must always return a NUL-terminated string */
    354  |     str[0] = '\0';
    355  |
    356  |     /* track current line length */
    357  |     line_len = 0;
    358  |
    359  |     /* get input if none available */
    360  |     if (condat->rd_buf_len <= 0) {
    361  |         if (sk_fill_rd_buf(condat) == -1) {
    362  |             return line_len;
    363  |         }
    364  |     }
    365  |
    366  |     for (;;) {
    367  |         /* we should always have something in the buffer here */
    368  |         dieif(condat->rd_buf_len <= 0);
    369  |
    370  |         /* check for end of line */
    371  |         eol = memchr(condat->rd_buf, '\n', condat->rd_buf_len);
    372  |
    373  |         /* figure out how much of the buffer to copy */
    374  |         if (eol == NULL) {
    375  |             /* no newline -> copy all available, if room */
    376  |             amt_to_copy = (count - line_len);
    377  |             if (amt_to_copy > condat->rd_buf_len) {
    378  |                 amt_to_copy = condat->rd_buf_len;
    379  |             }
    380  |         } else {
    381  |             /* newline -> copy line if room */
    382  |             amt_to_copy = eol - condat->rd_buf + 1;
    383  |             if (amt_to_copy > (count - line_len)) {
    384  |                 amt_to_copy = count - line_len;
    385  |             }
    386  |         }
    387  |
    388  |         /* copy data to string */
    389  |         memcpy(str + line_len, condat->rd_buf, amt_to_copy);
    390  |         line_len += amt_to_copy;
    391  |         str[line_len] = '\0';
    392  |
    393  |         /* remove data from input buffer */
    394  |         memmove(condat->rd_buf,
    395  |                 condat->rd_buf + amt_to_copy,
    396  |                 condat->rd_buf_len - amt_to_copy);
    397  |         condat->rd_buf_len -= amt_to_copy;
    398  |
    399  |         /* if we got a newline or our buffer is full, exit */
    400  |         if ((eol != NULL) || (line_len >= count)) {
    401  |             return line_len;
    402  |         }
    403  |
    404  |         /* otherwise get more input */
    405  |         if (sk_fill_rd_buf(condat) == -1) {
    406  |             return line_len;
    407  |         }
    408  |     }
    409  |
    410  | } /* SK_cd_gets() */
    411  |
    412  |
    413  | /*++++++++++++++++++++++++++++++++++++++
    414  |   Wrapper around the close(2) system call,
    415  |
    416  |   int SK_cd_close         returns the error codes of close(2).
    417  |
    418  |   sk_conn_st *condat      Pointer to the connection data structure.
    419  |
    420  |     +html+ <PRE>
    421  |   Author:
    422  |         marek
    423  |     +html+ </PRE>
    424  |   ++++++++++++++++++++++++++++++++++++++*/
    425  | int SK_cd_close(sk_conn_st *condat) {
    426  |   return SK_close(condat->sock);
    427  | } /* SK_cd_close() */
    428  |
    429  |
    430  | /* SK_cd_printf() */
    431  | /*++++++++++++++++++++++++++++++++++++++
    432  |
    433  |   Printf-like function to print to socket/file specified by connection
    434  |   data structure. First writes the text to a temporary buffer, then
    435  |   uses SK_cd_puts to print it. Maintains a 2K static buffer, and allocates
    436  |   more memory if this is not enough.
    437  |
    438  |   int SK_cd_printf       Returns the SK_cd_puts error code/return value.
    439  |
    440  |   sk_conn_st *condat     Pointer to the connection data structure.
    441  |
    442  |   char *txt              Format text to be written
    443  |
    444  |   ...                    more arguments (like printf)
    445  |
    446  |
    447  |     +html+ <PRE>
    448  |   Author:
    449  |         marek
    450  |     +html+ </PRE>
    451  |   ++++++++++++++++++++++++++++++++++++++*/
    452  | int SK_cd_printf(sk_conn_st *condat, char *txt, ...)
    453  | {
    454  | #define SKBUFLEN 2047
    455  |   va_list   ap;
    456  |   char      buffer[SKBUFLEN+1];
    457  |   unsigned  len;
    458  |   char      *newbuf = NULL;
    459  |   char      *finalbuf = buffer; /* points to where the text REALLY is */
    460  |
    461  |   /* vsnprintf returns the number of character it WOULD write if it could.
    462  |      So we assume the buffer to be of adequate size for most cases,
    463  |      and if it isn't, then we allocate to newbuf and call v*printf again
    464  |   */
    465  |   va_start(ap, txt);
    466  |   len = vsnprintf(buffer, SKBUFLEN, txt, ap);
    467  |   va_end(ap);
    468  |
    469  |   if( len > SKBUFLEN ) {
    470  |     newbuf = (char *)UT_malloc(len+1);
    471  |
    472  |     va_start(ap, txt);
    473  |     vsnprintf(newbuf, len+1, txt, ap);
    474  |     va_end(ap);
    475  |
    476  |     finalbuf = newbuf;
    477  |   }
    478  |   /* terminate */
    479  |   finalbuf[len] = 0;
    480  |
    481  |   /* reuse len */
    482  |   len = SK_cd_puts(condat, finalbuf);
    483  |
    484  |   if(newbuf != NULL) {
    485  |     UT_free(newbuf);
    486  |   }
    487  |
    488  |   return len;
    489  | } /* SK_cd_printf() */
    490  |