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 |