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