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 |