sockfilt.c (42093B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "first.h" 25 26 /* Purpose 27 * 28 * 1. Accept a TCP connection on a custom port (IPv4 or IPv6), or connect 29 * to a given (localhost) port. 30 * 31 * 2. Get commands on STDIN. Pass data on to the TCP stream. 32 * Get data from TCP stream and pass on to STDOUT. 33 * 34 * This program is made to perform all the socket/stream/connection stuff for 35 * the test suite's (perl) FTP server. Previously the perl code did all of 36 * this by its own, but I decided to let this program do the socket layer 37 * because of several things: 38 * 39 * o We want the perl code to work with rather old perl installations, thus 40 * we cannot use recent perl modules or features. 41 * 42 * o We want IPv6 support for systems that provide it, and doing optional IPv6 43 * support in perl seems if not impossible so at least awkward. 44 * 45 * o We want FTP-SSL support, which means that a connection that starts with 46 * plain sockets needs to be able to "go SSL" in the midst. This would also 47 * require some nasty perl stuff I'd rather avoid. 48 * 49 * (Source originally based on sws.c) 50 */ 51 52 /* 53 * Signal handling notes for sockfilt 54 * ---------------------------------- 55 * 56 * This program is a single-threaded process. 57 * 58 * This program is intended to be highly portable and as such it must be kept 59 * as simple as possible, due to this the only signal handling mechanisms used 60 * will be those of ANSI C, and used only in the most basic form which is good 61 * enough for the purpose of this program. 62 * 63 * For the above reason and the specific needs of this program signals SIGHUP, 64 * SIGPIPE and SIGALRM will be simply ignored on systems where this can be 65 * done. If possible, signals SIGINT and SIGTERM will be handled by this 66 * program as an indication to cleanup and finish execution as soon as 67 * possible. This will be achieved with a single signal handler 68 * 'exit_signal_handler' for both signals. 69 * 70 * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal 71 * will just set to one the global var 'got_exit_signal' storing in global var 72 * 'exit_signal' the signal that triggered this change. 73 * 74 * Nothing fancy that could introduce problems is used, the program at certain 75 * points in its normal flow checks if var 'got_exit_signal' is set and in 76 * case this is true it just makes its way out of loops and functions in 77 * structured and well behaved manner to achieve proper program cleanup and 78 * termination. 79 * 80 * Even with the above mechanism implemented it is worthwhile to note that 81 * other signals might still be received, or that there might be systems on 82 * which it is not possible to trap and ignore some of the above signals. 83 * This implies that for increased portability and reliability the program 84 * must be coded as if no signal was being ignored or handled at all. Enjoy 85 * it! 86 */ 87 88 /* buffer is this excessively large only to be able to support things like 89 test 1003 which tests exceedingly large server response lines */ 90 #define BUFFER_SIZE 17010 91 92 static bool verbose = FALSE; 93 static bool s_bind_only = FALSE; 94 static unsigned short server_connectport = 0; /* if non-zero, 95 we activate this mode */ 96 97 enum sockmode { 98 PASSIVE_LISTEN, /* as a server waiting for connections */ 99 PASSIVE_CONNECT, /* as a server, connected to a client */ 100 ACTIVE, /* as a client, connected to a server */ 101 ACTIVE_DISCONNECT /* as a client, disconnected from server */ 102 }; 103 104 #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 105 /* 106 * read-wrapper to support reading from stdin on Windows. 107 */ 108 static ssize_t read_wincon(int fd, void *buf, size_t count) 109 { 110 HANDLE handle = NULL; 111 DWORD mode, rcount = 0; 112 BOOL success; 113 114 if(fd == fileno(stdin)) { 115 handle = GetStdHandle(STD_INPUT_HANDLE); 116 } 117 else { 118 return read(fd, buf, count); 119 } 120 121 if(GetConsoleMode(handle, &mode)) { 122 success = ReadConsole(handle, buf, curlx_uztoul(count), &rcount, NULL); 123 } 124 else { 125 success = ReadFile(handle, buf, curlx_uztoul(count), &rcount, NULL); 126 } 127 if(success) { 128 return rcount; 129 } 130 131 CURL_SETERRNO((int)GetLastError()); 132 return -1; 133 } 134 135 /* 136 * write-wrapper to support writing to stdout and stderr on Windows. 137 */ 138 static ssize_t write_wincon(int fd, const void *buf, size_t count) 139 { 140 HANDLE handle = NULL; 141 DWORD mode, wcount = 0; 142 BOOL success; 143 144 if(fd == fileno(stdout)) { 145 handle = GetStdHandle(STD_OUTPUT_HANDLE); 146 } 147 else if(fd == fileno(stderr)) { 148 handle = GetStdHandle(STD_ERROR_HANDLE); 149 } 150 else { 151 return write(fd, buf, count); 152 } 153 154 if(GetConsoleMode(handle, &mode)) { 155 success = WriteConsole(handle, buf, curlx_uztoul(count), &wcount, NULL); 156 } 157 else { 158 success = WriteFile(handle, buf, curlx_uztoul(count), &wcount, NULL); 159 } 160 if(success) { 161 return wcount; 162 } 163 164 CURL_SETERRNO((int)GetLastError()); 165 return -1; 166 } 167 #define SOCKFILT_read read_wincon 168 #define SOCKFILT_write write_wincon 169 #else 170 #define SOCKFILT_read read 171 #define SOCKFILT_write write 172 #endif 173 174 #ifndef UNDER_CE 175 176 /* On Windows, we sometimes get this for a broken pipe, seemingly 177 * when the client just closed stdin? */ 178 #define CURL_WIN32_EPIPE 109 179 180 /* 181 * fullread is a wrapper around the read() function. This will repeat the call 182 * to read() until it actually has read the complete number of bytes indicated 183 * in nbytes or it fails with a condition that cannot be handled with a simple 184 * retry of the read call. 185 */ 186 187 static ssize_t fullread(int filedes, void *buffer, size_t nbytes) 188 { 189 int error; 190 ssize_t nread = 0; 191 192 do { 193 ssize_t rc = SOCKFILT_read(filedes, 194 (unsigned char *)buffer + nread, 195 nbytes - nread); 196 197 if(got_exit_signal) { 198 logmsg("signalled to die"); 199 return -1; 200 } 201 202 if(rc < 0) { 203 error = errno; 204 /* !checksrc! disable ERRNOVAR 1 */ 205 if((error == EINTR) || (error == EAGAIN)) 206 continue; 207 if(error == CURL_WIN32_EPIPE) { 208 logmsg("got Windows ERROR_BROKEN_PIPE on fd=%d, treating as close", 209 filedes); 210 return 0; 211 } 212 logmsg("reading from file descriptor: %d,", filedes); 213 logmsg("unrecoverable read() failure (%d) %s", 214 error, strerror(error)); 215 return -1; 216 } 217 218 if(rc == 0) { 219 logmsg("got 0 reading from stdin"); 220 return 0; 221 } 222 223 nread += rc; 224 225 } while((size_t)nread < nbytes); 226 227 if(verbose) 228 logmsg("read %zd bytes", nread); 229 230 return nread; 231 } 232 233 /* 234 * fullwrite is a wrapper around the write() function. This will repeat the 235 * call to write() until it actually has written the complete number of bytes 236 * indicated in nbytes or it fails with a condition that cannot be handled 237 * with a simple retry of the write call. 238 */ 239 240 static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) 241 { 242 int error; 243 ssize_t nwrite = 0; 244 245 do { 246 ssize_t wc = SOCKFILT_write(filedes, 247 (const unsigned char *)buffer + nwrite, 248 nbytes - nwrite); 249 250 if(got_exit_signal) { 251 logmsg("signalled to die"); 252 return -1; 253 } 254 255 if(wc < 0) { 256 error = errno; 257 /* !checksrc! disable ERRNOVAR 1 */ 258 if((error == EINTR) || (error == EAGAIN)) 259 continue; 260 logmsg("writing to file descriptor: %d,", filedes); 261 logmsg("unrecoverable write() failure (%d) %s", 262 error, strerror(error)); 263 return -1; 264 } 265 266 if(wc == 0) { 267 logmsg("put 0 writing to stdout"); 268 return 0; 269 } 270 271 nwrite += wc; 272 273 } while((size_t)nwrite < nbytes); 274 275 if(verbose) 276 logmsg("wrote %zd bytes", nwrite); 277 278 return nwrite; 279 } 280 281 /* 282 * read_stdin tries to read from stdin nbytes into the given buffer. This is a 283 * blocking function that will only return TRUE when nbytes have actually been 284 * read or FALSE when an unrecoverable error has been detected. Failure of this 285 * function is an indication that the sockfilt process should terminate. 286 */ 287 288 static bool read_stdin(void *buffer, size_t nbytes) 289 { 290 ssize_t nread = fullread(fileno(stdin), buffer, nbytes); 291 if(nread != (ssize_t)nbytes) { 292 logmsg("exiting..."); 293 return FALSE; 294 } 295 return TRUE; 296 } 297 #endif 298 299 /* 300 * write_stdout tries to write to stdio nbytes from the given buffer. This is a 301 * blocking function that will only return TRUE when nbytes have actually been 302 * written or FALSE when an unrecoverable error has been detected. Failure of 303 * this function is an indication that the sockfilt process should terminate. 304 */ 305 306 static bool write_stdout(const void *buffer, size_t nbytes) 307 { 308 ssize_t nwrite; 309 #ifdef UNDER_CE 310 puts(buffer); 311 nwrite = nbytes; 312 #else 313 nwrite = fullwrite(fileno(stdout), buffer, nbytes); 314 #endif 315 if(nwrite != (ssize_t)nbytes) { 316 logmsg("exiting..."); 317 return FALSE; 318 } 319 return TRUE; 320 } 321 322 #ifndef UNDER_CE 323 static void lograw(unsigned char *buffer, ssize_t len) 324 { 325 char data[120]; 326 ssize_t i; 327 unsigned char *ptr = buffer; 328 char *optr = data; 329 ssize_t width = 0; 330 int left = sizeof(data); 331 332 for(i = 0; i < len; i++) { 333 switch(ptr[i]) { 334 case '\n': 335 snprintf(optr, left, "\\n"); 336 width += 2; 337 optr += 2; 338 left -= 2; 339 break; 340 case '\r': 341 snprintf(optr, left, "\\r"); 342 width += 2; 343 optr += 2; 344 left -= 2; 345 break; 346 default: 347 snprintf(optr, left, "%c", (ISGRAPH(ptr[i]) || 348 ptr[i] == 0x20) ? ptr[i] : '.'); 349 width++; 350 optr++; 351 left--; 352 break; 353 } 354 355 if(width > 60) { 356 logmsg("'%s'", data); 357 width = 0; 358 optr = data; 359 left = sizeof(data); 360 } 361 } 362 if(width) 363 logmsg("'%s'", data); 364 } 365 366 /* 367 * handle the DATA command 368 * maxlen is the available space in buffer (input) 369 * *buffer_len is the amount of data in the buffer (output) 370 */ 371 static bool read_data_block(unsigned char *buffer, ssize_t maxlen, 372 ssize_t *buffer_len) 373 { 374 if(!read_stdin(buffer, 5)) 375 return FALSE; 376 377 buffer[5] = '\0'; 378 379 *buffer_len = (ssize_t)strtol((char *)buffer, NULL, 16); 380 if(*buffer_len > maxlen) { 381 logmsg("Buffer size (%zd bytes) too small for data size error " 382 "(%zd bytes)", maxlen, *buffer_len); 383 return FALSE; 384 } 385 logmsg("> %zd bytes data, server => client", *buffer_len); 386 387 if(!read_stdin(buffer, *buffer_len)) 388 return FALSE; 389 390 lograw(buffer, *buffer_len); 391 392 return TRUE; 393 } 394 #endif 395 396 397 #if defined(USE_WINSOCK) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 398 /* 399 * Winsock select() does not support standard file descriptors, 400 * it can only check SOCKETs. The following function is an attempt 401 * to re-create a select() function with support for other handle types. 402 * 403 * select() function with support for Winsock2 sockets and all 404 * other handle types supported by WaitForMultipleObjectsEx() as 405 * well as disk files, anonymous and names pipes, and character input. 406 * 407 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx 408 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx 409 */ 410 struct select_ws_wait_data { 411 HANDLE handle; /* actual handle to wait for during select */ 412 HANDLE signal; /* internal event to signal handle trigger */ 413 HANDLE abort; /* internal event to abort waiting threads */ 414 }; 415 #include <process.h> 416 static unsigned int WINAPI select_ws_wait_thread(void *lpParameter) 417 { 418 struct select_ws_wait_data *data; 419 HANDLE signal, handle, handles[2]; 420 INPUT_RECORD inputrecord; 421 LARGE_INTEGER size, pos; 422 DWORD type, length, ret; 423 424 /* retrieve handles from internal structure */ 425 data = (struct select_ws_wait_data *) lpParameter; 426 if(data) { 427 handle = data->handle; 428 handles[0] = data->abort; 429 handles[1] = handle; 430 signal = data->signal; 431 free(data); 432 } 433 else 434 return (DWORD)-1; 435 436 /* retrieve the type of file to wait on */ 437 type = GetFileType(handle); 438 switch(type) { 439 case FILE_TYPE_DISK: 440 /* The handle represents a file on disk, this means: 441 * - WaitForMultipleObjectsEx will always be signalled for it. 442 * - comparison of current position in file and total size of 443 * the file can be used to check if we reached the end yet. 444 * 445 * Approach: Loop till either the internal event is signalled 446 * or if the end of the file has already been reached. 447 */ 448 while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) 449 == WAIT_TIMEOUT) { 450 /* get total size of file */ 451 length = 0; 452 size.QuadPart = 0; 453 size.LowPart = GetFileSize(handle, &length); 454 if((size.LowPart != INVALID_FILE_SIZE) || 455 (GetLastError() == NO_ERROR)) { 456 size.HighPart = (LONG)length; 457 /* get the current position within the file */ 458 pos.QuadPart = 0; 459 pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart, FILE_CURRENT); 460 if((pos.LowPart != INVALID_SET_FILE_POINTER) || 461 (GetLastError() == NO_ERROR)) { 462 /* compare position with size, abort if not equal */ 463 if(size.QuadPart == pos.QuadPart) { 464 /* sleep and continue waiting */ 465 SleepEx(0, FALSE); 466 continue; 467 } 468 } 469 } 470 /* there is some data available, stop waiting */ 471 logmsg("[select_ws_wait_thread] data available, DISK: %p", handle); 472 SetEvent(signal); 473 } 474 break; 475 476 case FILE_TYPE_CHAR: 477 /* The handle represents a character input, this means: 478 * - WaitForMultipleObjectsEx will be signalled on any kind of input, 479 * including mouse and window size events we do not care about. 480 * 481 * Approach: Loop till either the internal event is signalled 482 * or we get signalled for an actual key-event. 483 */ 484 while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) 485 == WAIT_OBJECT_0 + 1) { 486 /* check if this is an actual console handle */ 487 if(GetConsoleMode(handle, &ret)) { 488 /* retrieve an event from the console buffer */ 489 length = 0; 490 if(PeekConsoleInput(handle, &inputrecord, 1, &length)) { 491 /* check if the event is not an actual key-event */ 492 if(length == 1 && inputrecord.EventType != KEY_EVENT) { 493 /* purge the non-key-event and continue waiting */ 494 ReadConsoleInput(handle, &inputrecord, 1, &length); 495 continue; 496 } 497 } 498 } 499 /* there is some data available, stop waiting */ 500 logmsg("[select_ws_wait_thread] data available, CHAR: %p", handle); 501 SetEvent(signal); 502 } 503 break; 504 505 case FILE_TYPE_PIPE: 506 /* The handle represents an anonymous or named pipe, this means: 507 * - WaitForMultipleObjectsEx will always be signalled for it. 508 * - peek into the pipe and retrieve the amount of data available. 509 * 510 * Approach: Loop till either the internal event is signalled 511 * or there is data in the pipe available for reading. 512 */ 513 while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) 514 == WAIT_TIMEOUT) { 515 /* peek into the pipe and retrieve the amount of data available */ 516 length = 0; 517 if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) { 518 /* if there is no data available, sleep and continue waiting */ 519 if(length == 0) { 520 SleepEx(0, FALSE); 521 continue; 522 } 523 else { 524 logmsg("[select_ws_wait_thread] PeekNamedPipe len: %lu", length); 525 } 526 } 527 else { 528 /* if the pipe has NOT been closed, sleep and continue waiting */ 529 ret = GetLastError(); 530 if(ret != ERROR_BROKEN_PIPE) { 531 logmsg("[select_ws_wait_thread] PeekNamedPipe error (%lu)", ret); 532 SleepEx(0, FALSE); 533 continue; 534 } 535 else { 536 logmsg("[select_ws_wait_thread] pipe closed, PIPE: %p", handle); 537 } 538 } 539 /* there is some data available, stop waiting */ 540 logmsg("[select_ws_wait_thread] data available, PIPE: %p", handle); 541 SetEvent(signal); 542 } 543 break; 544 545 default: 546 /* The handle has an unknown type, try to wait on it */ 547 if(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) 548 == WAIT_OBJECT_0 + 1) { 549 logmsg("[select_ws_wait_thread] data available, HANDLE: %p", handle); 550 SetEvent(signal); 551 } 552 break; 553 } 554 555 return 0; 556 } 557 558 static HANDLE select_ws_wait(HANDLE handle, HANDLE signal, HANDLE abort) 559 { 560 typedef uintptr_t curl_win_thread_handle_t; 561 struct select_ws_wait_data *data; 562 curl_win_thread_handle_t thread; 563 564 /* allocate internal waiting data structure */ 565 data = malloc(sizeof(struct select_ws_wait_data)); 566 if(data) { 567 data->handle = handle; 568 data->signal = signal; 569 data->abort = abort; 570 571 /* launch waiting thread */ 572 thread = _beginthreadex(NULL, 0, &select_ws_wait_thread, data, 0, NULL); 573 574 /* free data if thread failed to launch */ 575 if(!thread) { 576 free(data); 577 } 578 return (HANDLE)thread; 579 } 580 return NULL; 581 } 582 583 struct select_ws_data { 584 int fd; /* provided file descriptor (indexed by nfd) */ 585 long wsastate; /* internal pre-select state (indexed by nfd) */ 586 curl_socket_t wsasock; /* internal socket handle (indexed by nws) */ 587 WSAEVENT wsaevent; /* internal select event (indexed by nws) */ 588 HANDLE signal; /* internal thread signal (indexed by nth) */ 589 HANDLE thread; /* internal thread handle (indexed by nth) */ 590 }; 591 592 static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, 593 fd_set *exceptfds, struct timeval *tv) 594 { 595 DWORD timeout_ms, wait, nfd, nth, nws, i; 596 HANDLE abort, signal, handle, *handles; 597 fd_set readsock, writesock, exceptsock; 598 struct select_ws_data *data; 599 WSANETWORKEVENTS wsaevents; 600 curl_socket_t wsasock; 601 int error, ret, fd; 602 WSAEVENT wsaevent; 603 604 /* check if the input value is valid */ 605 if(nfds < 0) { 606 SET_SOCKERRNO(SOCKEINVAL); 607 return -1; 608 } 609 610 /* convert struct timeval to milliseconds */ 611 if(tv) { 612 timeout_ms = (DWORD)curlx_tvtoms(tv); 613 } 614 else { 615 timeout_ms = INFINITE; 616 } 617 618 /* check if we got descriptors, sleep in case we got none */ 619 if(!nfds) { 620 SleepEx(timeout_ms, FALSE); 621 return 0; 622 } 623 624 /* create internal event to abort waiting threads */ 625 abort = CreateEvent(NULL, TRUE, FALSE, NULL); 626 if(!abort) { 627 SET_SOCKERRNO(SOCKENOMEM); 628 return -1; 629 } 630 631 /* allocate internal array for the internal data */ 632 data = calloc(nfds, sizeof(struct select_ws_data)); 633 if(!data) { 634 CloseHandle(abort); 635 SET_SOCKERRNO(SOCKENOMEM); 636 return -1; 637 } 638 639 /* allocate internal array for the internal event handles */ 640 handles = calloc(nfds + 1, sizeof(HANDLE)); 641 if(!handles) { 642 CloseHandle(abort); 643 free(data); 644 SET_SOCKERRNO(SOCKENOMEM); 645 return -1; 646 } 647 648 /* loop over the handles in the input descriptor sets */ 649 nfd = 0; /* number of handled file descriptors */ 650 nth = 0; /* number of internal waiting threads */ 651 nws = 0; /* number of handled Winsock sockets */ 652 for(fd = 0; fd < nfds; fd++) { 653 wsasock = (curl_socket_t)fd; 654 wsaevents.lNetworkEvents = 0; 655 handles[nfd] = 0; 656 657 FD_ZERO(&readsock); 658 FD_ZERO(&writesock); 659 FD_ZERO(&exceptsock); 660 661 if(FD_ISSET(wsasock, readfds)) { 662 FD_SET(wsasock, &readsock); 663 wsaevents.lNetworkEvents |= FD_READ|FD_ACCEPT|FD_CLOSE; 664 } 665 666 if(FD_ISSET(wsasock, writefds)) { 667 FD_SET(wsasock, &writesock); 668 wsaevents.lNetworkEvents |= FD_WRITE|FD_CONNECT|FD_CLOSE; 669 } 670 671 if(FD_ISSET(wsasock, exceptfds)) { 672 FD_SET(wsasock, &exceptsock); 673 wsaevents.lNetworkEvents |= FD_OOB; 674 } 675 676 /* only wait for events for which we actually care */ 677 if(wsaevents.lNetworkEvents) { 678 data[nfd].fd = fd; 679 if(fd == fileno(stdin)) { 680 signal = CreateEvent(NULL, TRUE, FALSE, NULL); 681 if(signal) { 682 handle = GetStdHandle(STD_INPUT_HANDLE); 683 handle = select_ws_wait(handle, signal, abort); 684 if(handle) { 685 handles[nfd] = signal; 686 data[nth].signal = signal; 687 data[nth].thread = handle; 688 nfd++; 689 nth++; 690 } 691 else { 692 CloseHandle(signal); 693 } 694 } 695 } 696 else if(fd == fileno(stdout)) { 697 handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE); 698 nfd++; 699 } 700 else if(fd == fileno(stderr)) { 701 handles[nfd] = GetStdHandle(STD_ERROR_HANDLE); 702 nfd++; 703 } 704 else { 705 wsaevent = WSACreateEvent(); 706 if(wsaevent != WSA_INVALID_EVENT) { 707 if(wsaevents.lNetworkEvents & FD_WRITE) { 708 send(wsasock, NULL, 0, 0); /* reset FD_WRITE */ 709 } 710 error = WSAEventSelect(wsasock, wsaevent, wsaevents.lNetworkEvents); 711 if(error != SOCKET_ERROR) { 712 handles[nfd] = (HANDLE)wsaevent; 713 data[nws].wsasock = wsasock; 714 data[nws].wsaevent = wsaevent; 715 data[nfd].wsastate = 0; 716 tv->tv_sec = 0; 717 tv->tv_usec = 0; 718 /* check if the socket is already ready */ 719 if(select(fd + 1, &readsock, &writesock, &exceptsock, tv) == 1) { 720 logmsg("[select_ws] socket %d is ready", fd); 721 WSASetEvent(wsaevent); 722 if(FD_ISSET(wsasock, &readsock)) 723 data[nfd].wsastate |= FD_READ; 724 if(FD_ISSET(wsasock, &writesock)) 725 data[nfd].wsastate |= FD_WRITE; 726 if(FD_ISSET(wsasock, &exceptsock)) 727 data[nfd].wsastate |= FD_OOB; 728 } 729 nfd++; 730 nws++; 731 } 732 else { 733 WSACloseEvent(wsaevent); 734 signal = CreateEvent(NULL, TRUE, FALSE, NULL); 735 if(signal) { 736 handle = (HANDLE)wsasock; 737 handle = select_ws_wait(handle, signal, abort); 738 if(handle) { 739 handles[nfd] = signal; 740 data[nth].signal = signal; 741 data[nth].thread = handle; 742 nfd++; 743 nth++; 744 } 745 else { 746 CloseHandle(signal); 747 } 748 } 749 } 750 } 751 } 752 } 753 } 754 755 /* wait on the number of handles */ 756 wait = nfd; 757 758 /* make sure we stop waiting on exit signal event */ 759 if(exit_event) { 760 /* we allocated handles nfds + 1 for this */ 761 handles[nfd] = exit_event; 762 wait += 1; 763 } 764 765 /* wait for one of the internal handles to trigger */ 766 wait = WaitForMultipleObjectsEx(wait, handles, FALSE, timeout_ms, FALSE); 767 768 /* signal the abort event handle and join the other waiting threads */ 769 SetEvent(abort); 770 for(i = 0; i < nth; i++) { 771 WaitForSingleObjectEx(data[i].thread, INFINITE, FALSE); 772 CloseHandle(data[i].thread); 773 } 774 775 /* loop over the internal handles returned in the descriptors */ 776 ret = 0; /* number of ready file descriptors */ 777 for(i = 0; i < nfd; i++) { 778 fd = data[i].fd; 779 handle = handles[i]; 780 wsasock = (curl_socket_t)fd; 781 782 /* check if the current internal handle was triggered */ 783 if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= i && 784 WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) { 785 /* first handle stdin, stdout and stderr */ 786 if(fd == fileno(stdin)) { 787 /* stdin is never ready for write or exceptional */ 788 FD_CLR(wsasock, writefds); 789 FD_CLR(wsasock, exceptfds); 790 } 791 else if(fd == fileno(stdout) || fd == fileno(stderr)) { 792 /* stdout and stderr are never ready for read or exceptional */ 793 FD_CLR(wsasock, readfds); 794 FD_CLR(wsasock, exceptfds); 795 } 796 else { 797 /* try to handle the event with the Winsock2 functions */ 798 wsaevents.lNetworkEvents = 0; 799 error = WSAEnumNetworkEvents(wsasock, handle, &wsaevents); 800 if(error != SOCKET_ERROR) { 801 /* merge result from pre-check using select */ 802 wsaevents.lNetworkEvents |= data[i].wsastate; 803 804 /* remove from descriptor set if not ready for read/accept/close */ 805 if(!(wsaevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))) 806 FD_CLR(wsasock, readfds); 807 808 /* remove from descriptor set if not ready for write/connect */ 809 if(!(wsaevents.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE))) 810 FD_CLR(wsasock, writefds); 811 812 /* remove from descriptor set if not exceptional */ 813 if(!(wsaevents.lNetworkEvents & FD_OOB)) 814 FD_CLR(wsasock, exceptfds); 815 } 816 } 817 818 /* check if the event has not been filtered using specific tests */ 819 if(FD_ISSET(wsasock, readfds) || FD_ISSET(wsasock, writefds) || 820 FD_ISSET(wsasock, exceptfds)) { 821 ret++; 822 } 823 } 824 else { 825 /* remove from all descriptor sets since this handle did not trigger */ 826 FD_CLR(wsasock, readfds); 827 FD_CLR(wsasock, writefds); 828 FD_CLR(wsasock, exceptfds); 829 } 830 } 831 832 for(fd = 0; fd < nfds; fd++) { 833 if(FD_ISSET(fd, readfds)) 834 logmsg("[select_ws] %d is readable", fd); 835 if(FD_ISSET(fd, writefds)) 836 logmsg("[select_ws] %d is writable", fd); 837 if(FD_ISSET(fd, exceptfds)) 838 logmsg("[select_ws] %d is exceptional", fd); 839 } 840 841 for(i = 0; i < nws; i++) { 842 WSAEventSelect(data[i].wsasock, NULL, 0); 843 WSACloseEvent(data[i].wsaevent); 844 } 845 846 for(i = 0; i < nth; i++) { 847 CloseHandle(data[i].signal); 848 } 849 CloseHandle(abort); 850 851 free(handles); 852 free(data); 853 854 return ret; 855 } 856 #define SOCKFILT_select(a,b,c,d,e) select_ws(a,b,c,d,e) 857 #else 858 #define SOCKFILT_select(a,b,c,d,e) select(a,b,c,d,e) 859 #endif /* USE_WINSOCK */ 860 861 862 #ifndef UNDER_CE 863 /* Perform the disconnect handshake with sockfilt 864 * This involves waiting for the disconnect acknowledgment after the DISC 865 * command, while throwing away anything else that might come in before 866 * that. 867 */ 868 static bool disc_handshake(void) 869 { 870 if(!write_stdout("DISC\n", 5)) 871 return FALSE; 872 873 do { 874 unsigned char buffer[BUFFER_SIZE]; 875 ssize_t buffer_len; 876 if(!read_stdin(buffer, 5)) 877 return FALSE; 878 logmsg("Received %c%c%c%c (on stdin)", 879 buffer[0], buffer[1], buffer[2], buffer[3]); 880 881 if(!memcmp("ACKD", buffer, 4)) { 882 /* got the ack we were waiting for */ 883 break; 884 } 885 else if(!memcmp("DISC", buffer, 4)) { 886 logmsg("Crikey! Client also wants to disconnect"); 887 if(!write_stdout("ACKD\n", 5)) 888 return FALSE; 889 } 890 else if(!memcmp("DATA", buffer, 4)) { 891 /* We must read more data to stay in sync */ 892 logmsg("Throwing away data bytes"); 893 if(!read_data_block(buffer, sizeof(buffer), &buffer_len)) 894 return FALSE; 895 896 } 897 else if(!memcmp("QUIT", buffer, 4)) { 898 /* just die */ 899 logmsg("quits"); 900 return FALSE; 901 } 902 else { 903 logmsg("Unexpected message error; aborting"); 904 /* 905 * The only other messages that could occur here are PING and PORT, 906 * and both of them occur at the start of a test when nothing should be 907 * trying to DISC. Therefore, we should not ever get here, but if we 908 * do, it's probably due to some kind of unclean shutdown situation so 909 * us shutting down is what we probably ought to be doing, anyway. 910 */ 911 return FALSE; 912 } 913 914 } while(TRUE); 915 return TRUE; 916 } 917 #endif 918 919 /* 920 sockfdp is a pointer to an established stream or CURL_SOCKET_BAD 921 922 if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must 923 accept() 924 */ 925 static bool juggle(curl_socket_t *sockfdp, 926 curl_socket_t listenfd, 927 enum sockmode *mode) 928 { 929 #ifdef UNDER_CE 930 (void)sockfdp; 931 (void)listenfd; 932 (void)mode; 933 return FALSE; 934 #else 935 struct timeval timeout; 936 fd_set fds_read; 937 fd_set fds_write; 938 fd_set fds_err; 939 curl_socket_t sockfd = CURL_SOCKET_BAD; 940 int maxfd = -99; 941 ssize_t rc; 942 int error = 0; 943 944 unsigned char buffer[BUFFER_SIZE]; 945 char data[16]; 946 947 if(got_exit_signal) { 948 logmsg("signalled to die, exiting..."); 949 return FALSE; 950 } 951 952 #ifdef HAVE_GETPPID 953 /* As a last resort, quit if sockfilt process becomes orphan. Just in case 954 parent ftpserver process has died without killing its sockfilt children */ 955 if(getppid() <= 1) { 956 logmsg("process becomes orphan, exiting"); 957 return FALSE; 958 } 959 #endif 960 961 timeout.tv_sec = 120; 962 timeout.tv_usec = 0; 963 964 FD_ZERO(&fds_read); 965 FD_ZERO(&fds_write); 966 FD_ZERO(&fds_err); 967 968 #if defined(__DJGPP__) 969 #pragma GCC diagnostic push 970 #pragma GCC diagnostic ignored "-Warith-conversion" 971 #endif 972 FD_SET((curl_socket_t)fileno(stdin), &fds_read); 973 #if defined(__DJGPP__) 974 #pragma GCC diagnostic pop 975 #endif 976 977 switch(*mode) { 978 979 case PASSIVE_LISTEN: 980 981 /* server mode */ 982 sockfd = listenfd; 983 /* there's always a socket to wait for */ 984 #if defined(__DJGPP__) 985 #pragma GCC diagnostic push 986 #pragma GCC diagnostic ignored "-Warith-conversion" 987 #endif 988 FD_SET(sockfd, &fds_read); 989 #if defined(__DJGPP__) 990 #pragma GCC diagnostic pop 991 #endif 992 maxfd = (int)sockfd; 993 break; 994 995 case PASSIVE_CONNECT: 996 997 sockfd = *sockfdp; 998 if(CURL_SOCKET_BAD == sockfd) { 999 /* eeek, we are supposedly connected and then this cannot be -1 ! */ 1000 logmsg("socket is -1! on %s:%d", __FILE__, __LINE__); 1001 maxfd = 0; /* stdin */ 1002 } 1003 else { 1004 /* there's always a socket to wait for */ 1005 #if defined(__DJGPP__) 1006 #pragma GCC diagnostic push 1007 #pragma GCC diagnostic ignored "-Warith-conversion" 1008 #endif 1009 FD_SET(sockfd, &fds_read); 1010 #if defined(__DJGPP__) 1011 #pragma GCC diagnostic pop 1012 #endif 1013 maxfd = (int)sockfd; 1014 } 1015 break; 1016 1017 case ACTIVE: 1018 1019 sockfd = *sockfdp; 1020 /* sockfd turns CURL_SOCKET_BAD when our connection has been closed */ 1021 if(CURL_SOCKET_BAD != sockfd) { 1022 #if defined(__DJGPP__) 1023 #pragma GCC diagnostic push 1024 #pragma GCC diagnostic ignored "-Warith-conversion" 1025 #endif 1026 FD_SET(sockfd, &fds_read); 1027 #if defined(__DJGPP__) 1028 #pragma GCC diagnostic pop 1029 #endif 1030 maxfd = (int)sockfd; 1031 } 1032 else { 1033 logmsg("No socket to read on"); 1034 maxfd = 0; 1035 } 1036 break; 1037 1038 case ACTIVE_DISCONNECT: 1039 1040 logmsg("disconnected, no socket to read on"); 1041 maxfd = 0; 1042 sockfd = CURL_SOCKET_BAD; 1043 break; 1044 1045 } /* switch(*mode) */ 1046 1047 1048 do { 1049 1050 /* select() blocking behavior call on blocking descriptors please */ 1051 1052 rc = SOCKFILT_select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); 1053 1054 if(got_exit_signal) { 1055 logmsg("signalled to die, exiting..."); 1056 return FALSE; 1057 } 1058 1059 } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR)); 1060 1061 if(rc < 0) { 1062 logmsg("select() failed with error (%d) %s", 1063 error, sstrerror(error)); 1064 return FALSE; 1065 } 1066 1067 if(rc == 0) 1068 /* timeout */ 1069 return TRUE; 1070 1071 1072 if(FD_ISSET(fileno(stdin), &fds_read)) { 1073 ssize_t buffer_len; 1074 /* read from stdin, commands/data to be dealt with and possibly passed on 1075 to the socket 1076 1077 protocol: 1078 1079 4 letter command + LF [mandatory] 1080 1081 4-digit hexadecimal data length + LF [if the command takes data] 1082 data [the data being as long as set above] 1083 1084 Commands: 1085 1086 DATA - plain pass-through data 1087 */ 1088 1089 if(!read_stdin(buffer, 5)) 1090 return FALSE; 1091 1092 logmsg("Received %c%c%c%c (on stdin)", 1093 buffer[0], buffer[1], buffer[2], buffer[3]); 1094 1095 if(!memcmp("PING", buffer, 4)) { 1096 /* send reply on stdout, just proving we are alive */ 1097 if(!write_stdout("PONG\n", 5)) 1098 return FALSE; 1099 } 1100 1101 else if(!memcmp("PORT", buffer, 4)) { 1102 /* Question asking us what PORT number we are listening to. 1103 Replies to PORT with "IPv[num]/[port]" */ 1104 snprintf((char *)buffer, sizeof(buffer), "%s/%hu\n", 1105 ipv_inuse, server_port); 1106 buffer_len = (ssize_t)strlen((char *)buffer); 1107 snprintf(data, sizeof(data), "PORT\n%04x\n", (int)buffer_len); 1108 if(!write_stdout(data, 10)) 1109 return FALSE; 1110 if(!write_stdout(buffer, buffer_len)) 1111 return FALSE; 1112 } 1113 else if(!memcmp("QUIT", buffer, 4)) { 1114 /* just die */ 1115 logmsg("quits"); 1116 return FALSE; 1117 } 1118 else if(!memcmp("DATA", buffer, 4)) { 1119 /* data IN => data OUT */ 1120 if(!read_data_block(buffer, sizeof(buffer), &buffer_len)) 1121 return FALSE; 1122 1123 if(buffer_len < 0) 1124 return FALSE; 1125 1126 if(*mode == PASSIVE_LISTEN) { 1127 logmsg("*** We are disconnected!"); 1128 if(!disc_handshake()) 1129 return FALSE; 1130 } 1131 else { 1132 /* send away on the socket */ 1133 ssize_t bytes_written = swrite(sockfd, buffer, buffer_len); 1134 if(bytes_written != buffer_len) { 1135 logmsg("Not all data was sent. Bytes to send: %zd sent: %zd", 1136 buffer_len, bytes_written); 1137 } 1138 } 1139 } 1140 else if(!memcmp("DISC", buffer, 4)) { 1141 /* disconnect! */ 1142 if(!write_stdout("ACKD\n", 5)) 1143 return FALSE; 1144 if(sockfd != CURL_SOCKET_BAD) { 1145 logmsg("====> Client forcibly disconnected"); 1146 sclose(sockfd); 1147 *sockfdp = CURL_SOCKET_BAD; 1148 if(*mode == PASSIVE_CONNECT) 1149 *mode = PASSIVE_LISTEN; 1150 else 1151 *mode = ACTIVE_DISCONNECT; 1152 } 1153 else 1154 logmsg("attempt to close already dead connection"); 1155 return TRUE; 1156 } 1157 } 1158 1159 1160 if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) { 1161 ssize_t nread_socket; 1162 if(*mode == PASSIVE_LISTEN) { 1163 /* there's no stream set up yet, this is an indication that there's a 1164 client connecting. */ 1165 curl_socket_t newfd = accept(sockfd, NULL, NULL); 1166 if(CURL_SOCKET_BAD == newfd) { 1167 error = SOCKERRNO; 1168 logmsg("accept() failed with error (%d) %s", error, sstrerror(error)); 1169 } 1170 else { 1171 logmsg("====> Client connect"); 1172 if(!write_stdout("CNCT\n", 5)) 1173 return FALSE; 1174 *sockfdp = newfd; /* store the new socket */ 1175 *mode = PASSIVE_CONNECT; /* we have connected */ 1176 } 1177 return TRUE; 1178 } 1179 1180 /* read from socket, pass on data to stdout */ 1181 nread_socket = sread(sockfd, buffer, sizeof(buffer)); 1182 1183 if(nread_socket > 0) { 1184 snprintf(data, sizeof(data), "DATA\n%04x\n", (int)nread_socket); 1185 if(!write_stdout(data, 10)) 1186 return FALSE; 1187 if(!write_stdout(buffer, nread_socket)) 1188 return FALSE; 1189 1190 logmsg("< %zd bytes data, client => server", nread_socket); 1191 lograw(buffer, nread_socket); 1192 } 1193 1194 if(nread_socket <= 0) { 1195 logmsg("====> Client disconnect"); 1196 if(!disc_handshake()) 1197 return FALSE; 1198 sclose(sockfd); 1199 *sockfdp = CURL_SOCKET_BAD; 1200 if(*mode == PASSIVE_CONNECT) 1201 *mode = PASSIVE_LISTEN; 1202 else 1203 *mode = ACTIVE_DISCONNECT; 1204 return TRUE; 1205 } 1206 } 1207 1208 return TRUE; 1209 #endif 1210 } 1211 1212 static int test_sockfilt(int argc, char *argv[]) 1213 { 1214 srvr_sockaddr_union_t me; 1215 curl_socket_t sock = CURL_SOCKET_BAD; 1216 curl_socket_t msgsock = CURL_SOCKET_BAD; 1217 int wrotepidfile = 0; 1218 int wroteportfile = 0; 1219 bool juggle_again; 1220 int rc; 1221 int error; 1222 int arg = 1; 1223 enum sockmode mode = PASSIVE_LISTEN; /* default */ 1224 const char *addr = NULL; 1225 1226 pidname = ".sockfilt.pid"; 1227 serverlogfile = "log/sockfilt.log"; 1228 server_port = 8999; 1229 1230 while(argc > arg) { 1231 if(!strcmp("--version", argv[arg])) { 1232 printf("sockfilt IPv4%s\n", 1233 #ifdef USE_IPV6 1234 "/IPv6" 1235 #else 1236 "" 1237 #endif 1238 ); 1239 return 0; 1240 } 1241 else if(!strcmp("--verbose", argv[arg])) { 1242 verbose = TRUE; 1243 arg++; 1244 } 1245 else if(!strcmp("--pidfile", argv[arg])) { 1246 arg++; 1247 if(argc > arg) 1248 pidname = argv[arg++]; 1249 } 1250 else if(!strcmp("--portfile", argv[arg])) { 1251 arg++; 1252 if(argc > arg) 1253 portname = argv[arg++]; 1254 } 1255 else if(!strcmp("--logfile", argv[arg])) { 1256 arg++; 1257 if(argc > arg) 1258 serverlogfile = argv[arg++]; 1259 } 1260 else if(!strcmp("--ipv6", argv[arg])) { 1261 #ifdef USE_IPV6 1262 socket_domain = AF_INET6; 1263 ipv_inuse = "IPv6"; 1264 #endif 1265 arg++; 1266 } 1267 else if(!strcmp("--ipv4", argv[arg])) { 1268 /* for completeness, we support this option as well */ 1269 #ifdef USE_IPV6 1270 socket_domain = AF_INET; 1271 ipv_inuse = "IPv4"; 1272 #endif 1273 arg++; 1274 } 1275 else if(!strcmp("--bindonly", argv[arg])) { 1276 s_bind_only = TRUE; 1277 arg++; 1278 } 1279 else if(!strcmp("--port", argv[arg])) { 1280 arg++; 1281 if(argc > arg) { 1282 char *endptr; 1283 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1284 server_port = util_ultous(ulnum); 1285 arg++; 1286 } 1287 } 1288 else if(!strcmp("--connect", argv[arg])) { 1289 /* Asked to actively connect to the specified local port instead of 1290 doing a passive server-style listening. */ 1291 arg++; 1292 if(argc > arg) { 1293 char *endptr; 1294 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1295 if((endptr != argv[arg] + strlen(argv[arg])) || 1296 (ulnum < 1025UL) || (ulnum > 65535UL)) { 1297 fprintf(stderr, "sockfilt: invalid --connect argument (%s)\n", 1298 argv[arg]); 1299 return 0; 1300 } 1301 server_connectport = util_ultous(ulnum); 1302 arg++; 1303 } 1304 } 1305 else if(!strcmp("--addr", argv[arg])) { 1306 /* Set an IP address to use with --connect; otherwise use localhost */ 1307 arg++; 1308 if(argc > arg) { 1309 addr = argv[arg]; 1310 arg++; 1311 } 1312 } 1313 else { 1314 puts("Usage: sockfilt [option]\n" 1315 " --version\n" 1316 " --verbose\n" 1317 " --logfile [file]\n" 1318 " --pidfile [file]\n" 1319 " --portfile [file]\n" 1320 " --ipv4\n" 1321 " --ipv6\n" 1322 " --bindonly\n" 1323 " --port [port]\n" 1324 " --connect [port]\n" 1325 " --addr [address]"); 1326 return 0; 1327 } 1328 } 1329 1330 #ifdef _WIN32 1331 if(win32_init()) 1332 return 2; 1333 #endif 1334 1335 CURLX_SET_BINMODE(stdin); 1336 CURLX_SET_BINMODE(stdout); 1337 CURLX_SET_BINMODE(stderr); 1338 1339 install_signal_handlers(false); 1340 1341 sock = socket(socket_domain, SOCK_STREAM, 0); 1342 1343 if(CURL_SOCKET_BAD == sock) { 1344 error = SOCKERRNO; 1345 logmsg("Error creating socket (%d) %s", error, sstrerror(error)); 1346 write_stdout("FAIL\n", 5); 1347 goto sockfilt_cleanup; 1348 } 1349 1350 if(server_connectport) { 1351 /* Active mode, we should connect to the given port number */ 1352 mode = ACTIVE; 1353 switch(socket_domain) { 1354 case AF_INET: 1355 memset(&me.sa4, 0, sizeof(me.sa4)); 1356 me.sa4.sin_family = AF_INET; 1357 me.sa4.sin_port = htons(server_connectport); 1358 me.sa4.sin_addr.s_addr = INADDR_ANY; 1359 if(!addr) 1360 addr = "127.0.0.1"; 1361 curlx_inet_pton(AF_INET, addr, &me.sa4.sin_addr); 1362 1363 rc = connect(sock, &me.sa, sizeof(me.sa4)); 1364 break; 1365 #ifdef USE_IPV6 1366 case AF_INET6: 1367 memset(&me.sa6, 0, sizeof(me.sa6)); 1368 me.sa6.sin6_family = AF_INET6; 1369 me.sa6.sin6_port = htons(server_connectport); 1370 if(!addr) 1371 addr = "::1"; 1372 curlx_inet_pton(AF_INET6, addr, &me.sa6.sin6_addr); 1373 1374 rc = connect(sock, &me.sa, sizeof(me.sa6)); 1375 break; 1376 #endif /* USE_IPV6 */ 1377 default: 1378 rc = 1; 1379 } 1380 if(rc) { 1381 error = SOCKERRNO; 1382 logmsg("Error connecting to port %hu (%d) %s", 1383 server_connectport, error, sstrerror(error)); 1384 write_stdout("FAIL\n", 5); 1385 goto sockfilt_cleanup; 1386 } 1387 logmsg("====> Client connect"); 1388 msgsock = sock; /* use this as stream */ 1389 } 1390 else { 1391 /* passive daemon style */ 1392 sock = sockdaemon(sock, &server_port, NULL, s_bind_only); 1393 if(CURL_SOCKET_BAD == sock) { 1394 write_stdout("FAIL\n", 5); 1395 goto sockfilt_cleanup; 1396 } 1397 msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ 1398 } 1399 1400 logmsg("Running %s version", ipv_inuse); 1401 1402 if(server_connectport) 1403 logmsg("Connected to port %hu", server_connectport); 1404 else if(s_bind_only) 1405 logmsg("Bound without listening on port %hu", server_port); 1406 else 1407 logmsg("Listening on port %hu", server_port); 1408 1409 wrotepidfile = write_pidfile(pidname); 1410 if(!wrotepidfile) { 1411 write_stdout("FAIL\n", 5); 1412 goto sockfilt_cleanup; 1413 } 1414 if(portname) { 1415 wroteportfile = write_portfile(portname, server_port); 1416 if(!wroteportfile) { 1417 write_stdout("FAIL\n", 5); 1418 goto sockfilt_cleanup; 1419 } 1420 } 1421 1422 do { 1423 juggle_again = juggle(&msgsock, sock, &mode); 1424 } while(juggle_again); 1425 1426 sockfilt_cleanup: 1427 1428 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) 1429 sclose(msgsock); 1430 1431 if(sock != CURL_SOCKET_BAD) 1432 sclose(sock); 1433 1434 if(wrotepidfile) 1435 unlink(pidname); 1436 if(wroteportfile) 1437 unlink(portname); 1438 1439 restore_signal_handlers(false); 1440 1441 if(got_exit_signal) { 1442 logmsg("============> sockfilt exits with signal (%d)", exit_signal); 1443 /* 1444 * To properly set the return status of the process we 1445 * must raise the same signal SIGINT or SIGTERM that we 1446 * caught and let the old handler take care of it. 1447 */ 1448 raise(exit_signal); 1449 } 1450 1451 logmsg("============> sockfilt quits"); 1452 return 0; 1453 }