/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- SK_cd_make
- SK_cd_free
- SK_cd_puts
- sk_fill_rd_buf
- main
- SK_cd_gets
- SK_cd_close
- SK_cd_printf
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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 ()
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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) {
/* [<][>][^][v][top][bottom][index][help] */
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, ...)
/* [<][>][^][v][top][bottom][index][help] */
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