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  |