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 "server_setup.h"
25 #include <stdlib.h>
26
27 /* Function
28 *
29 * Accepts a TCP connection on a custom port (IPv4 or IPv6). Connects to a
30 * given addr + port backend (that is NOT extracted form the client's
31 * request). The backend server default to connect to can be set with
32 * --backend and --backendport.
33 *
34 * Read commands from FILE (set with --config). The commands control how to
35 * act and is reset to defaults each client TCP connect.
36 *
37 * Config file keywords:
38 *
39 * "version [number: 5]" - requires the communication to use this version.
40 * "nmethods_min [number: 1]" - the minimum numberf NMETHODS the client must
41 * state
42 * "nmethods_max [number: 3]" - the minimum numberf NMETHODS the client must
43 * state
44 * "user [string]" - the user name that must match (if method is 2)
45 * "password [string]" - the password that must match (if method is 2)
46 * "backend [IPv4]" - numerical IPv4 address of backend to connect to
47 * "backendport [number:0]" - TCP port of backend to connect to. 0 means use
48 the client's specified port number.
49 * "method [number: 0]" - connect method to respond with:
50 * 0 - no auth
51 * 1 - GSSAPI (not supported)
52 * 2 - user + password
53 * "response [number]" - the decimal number to respond to a connect
54 * SOCKS5: 0 is OK, SOCKS4: 90 is ok
55 *
56 */
57
58 /* based on sockfilt.c */
59
60 #include <signal.h>
61 #ifdef HAVE_NETINET_IN_H
62 #include <netinet/in.h>
63 #endif
64 #ifdef HAVE_NETINET_IN6_H
65 #include <netinet/in6.h>
66 #endif
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif
70 #ifdef HAVE_NETDB_H
71 #include <netdb.h>
72 #endif
73
74 #define ENABLE_CURLX_PRINTF
75 /* make the curlx header define all printf() functions to use the curlx_*
76 versions instead */
77 #include "curlx.h" /* from the private lib dir */
78 #include "getpart.h"
79 #include "inet_pton.h"
80 #include "util.h"
81 #include "server_sockaddr.h"
82 #include "warnless.h"
83
84 /* include memdebug.h last */
85 #include "memdebug.h"
86
87 #ifdef USE_WINSOCK
88 #undef EINTR
89 #define EINTR 4 /* errno.h value */
90 #undef EAGAIN
91 #define EAGAIN 11 /* errno.h value */
92 #undef ENOMEM
93 #define ENOMEM 12 /* errno.h value */
94 #undef EINVAL
95 #define EINVAL 22 /* errno.h value */
96 #endif
97
98 #define DEFAULT_PORT 8905
99
100 #ifndef DEFAULT_LOGFILE
101 #define DEFAULT_LOGFILE "log/socksd.log"
102 #endif
103
104 #ifndef DEFAULT_REQFILE
105 #define DEFAULT_REQFILE "log/socksd-request.log"
106 #endif
107
108 #ifndef DEFAULT_CONFIG
109 #define DEFAULT_CONFIG "socksd.config"
110 #endif
111
112 static const char *backendaddr = "127.0.0.1";
113 static unsigned short backendport = 0; /* default is use client's */
114
115 struct configurable {
116 unsigned char version; /* initial version byte in the request must match
117 this */
118 unsigned char nmethods_min; /* minimum number of nmethods to expect */
119 unsigned char nmethods_max; /* maximum number of nmethods to expect */
120 unsigned char responseversion;
121 unsigned char responsemethod;
122 unsigned char reqcmd;
123 unsigned char connectrep;
124 unsigned short port; /* backend port */
125 char addr[32]; /* backend IPv4 numerical */
126 char user[256];
127 char password[256];
128 };
129
130 #define CONFIG_VERSION 5
131 #define CONFIG_NMETHODS_MIN 1 /* unauth, gssapi, auth */
132 #define CONFIG_NMETHODS_MAX 3
133 #define CONFIG_RESPONSEVERSION CONFIG_VERSION
134 #define CONFIG_RESPONSEMETHOD 0 /* no auth */
135 #define CONFIG_REQCMD 1 /* CONNECT */
136 #define CONFIG_PORT backendport
137 #define CONFIG_ADDR backendaddr
138 #define CONFIG_CONNECTREP 0
139
140 static struct configurable config;
141
142 const char *serverlogfile = DEFAULT_LOGFILE;
143 static const char *reqlogfile = DEFAULT_REQFILE;
144 static const char *configfile = DEFAULT_CONFIG;
145
146 static const char *socket_type = "IPv4";
147 static unsigned short port = DEFAULT_PORT;
148
resetdefaults(void)149 static void resetdefaults(void)
150 {
151 logmsg("Reset to defaults");
152 config.version = CONFIG_VERSION;
153 config.nmethods_min = CONFIG_NMETHODS_MIN;
154 config.nmethods_max = CONFIG_NMETHODS_MAX;
155 config.responseversion = CONFIG_RESPONSEVERSION;
156 config.responsemethod = CONFIG_RESPONSEMETHOD;
157 config.reqcmd = CONFIG_REQCMD;
158 config.connectrep = CONFIG_CONNECTREP;
159 config.port = CONFIG_PORT;
160 strcpy(config.addr, CONFIG_ADDR);
161 strcpy(config.user, "user");
162 strcpy(config.password, "password");
163 }
164
byteval(char * value)165 static unsigned char byteval(char *value)
166 {
167 unsigned long num = strtoul(value, NULL, 10);
168 return num & 0xff;
169 }
170
shortval(char * value)171 static unsigned short shortval(char *value)
172 {
173 unsigned long num = strtoul(value, NULL, 10);
174 return num & 0xffff;
175 }
176
177 static enum {
178 socket_domain_inet = AF_INET
179 #ifdef USE_IPV6
180 , socket_domain_inet6 = AF_INET6
181 #endif
182 #ifdef USE_UNIX_SOCKETS
183 , socket_domain_unix = AF_UNIX
184 #endif
185 } socket_domain = AF_INET;
186
getconfig(void)187 static void getconfig(void)
188 {
189 FILE *fp = fopen(configfile, FOPEN_READTEXT);
190 resetdefaults();
191 if(fp) {
192 char buffer[512];
193 logmsg("parse config file");
194 while(fgets(buffer, sizeof(buffer), fp)) {
195 char key[32];
196 char value[260];
197 if(2 == sscanf(buffer, "%31s %259s", key, value)) {
198 if(!strcmp(key, "version")) {
199 config.version = byteval(value);
200 logmsg("version [%d] set", config.version);
201 }
202 else if(!strcmp(key, "nmethods_min")) {
203 config.nmethods_min = byteval(value);
204 logmsg("nmethods_min [%d] set", config.nmethods_min);
205 }
206 else if(!strcmp(key, "nmethods_max")) {
207 config.nmethods_max = byteval(value);
208 logmsg("nmethods_max [%d] set", config.nmethods_max);
209 }
210 else if(!strcmp(key, "backend")) {
211 strcpy(config.addr, value);
212 logmsg("backend [%s] set", config.addr);
213 }
214 else if(!strcmp(key, "backendport")) {
215 config.port = shortval(value);
216 logmsg("backendport [%d] set", config.port);
217 }
218 else if(!strcmp(key, "user")) {
219 strcpy(config.user, value);
220 logmsg("user [%s] set", config.user);
221 }
222 else if(!strcmp(key, "password")) {
223 strcpy(config.password, value);
224 logmsg("password [%s] set", config.password);
225 }
226 /* Methods:
227 o X'00' NO AUTHENTICATION REQUIRED
228 o X'01' GSSAPI
229 o X'02' USERNAME/PASSWORD
230 */
231 else if(!strcmp(key, "method")) {
232 config.responsemethod = byteval(value);
233 logmsg("method [%d] set", config.responsemethod);
234 }
235 else if(!strcmp(key, "response")) {
236 config.connectrep = byteval(value);
237 logmsg("response [%d] set", config.connectrep);
238 }
239 }
240 }
241 fclose(fp);
242 }
243 }
244
loghex(unsigned char * buffer,ssize_t len)245 static void loghex(unsigned char *buffer, ssize_t len)
246 {
247 char data[1200];
248 ssize_t i;
249 unsigned char *ptr = buffer;
250 char *optr = data;
251 ssize_t width = 0;
252 int left = sizeof(data);
253
254 for(i = 0; i<len && (left >= 0); i++) {
255 msnprintf(optr, left, "%02x", ptr[i]);
256 width += 2;
257 optr += 2;
258 left -= 2;
259 }
260 if(width)
261 logmsg("'%s'", data);
262 }
263
264 /* RFC 1928, SOCKS5 byte index */
265 #define SOCKS5_VERSION 0
266 #define SOCKS5_NMETHODS 1 /* number of methods that is listed */
267
268 /* in the request: */
269 #define SOCKS5_REQCMD 1
270 #define SOCKS5_RESERVED 2
271 #define SOCKS5_ATYP 3
272 #define SOCKS5_DSTADDR 4
273
274 /* connect response */
275 #define SOCKS5_REP 1
276 #define SOCKS5_BNDADDR 4
277
278 /* auth request */
279 #define SOCKS5_ULEN 1
280 #define SOCKS5_UNAME 2
281
282 #define SOCKS4_CD 1
283 #define SOCKS4_DSTPORT 2
284
285 /* connect to a given IPv4 address, not the one asked for */
socksconnect(unsigned short connectport,const char * connectaddr)286 static curl_socket_t socksconnect(unsigned short connectport,
287 const char *connectaddr)
288 {
289 int rc;
290 srvr_sockaddr_union_t me;
291 curl_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
292 if(sock == CURL_SOCKET_BAD)
293 return CURL_SOCKET_BAD;
294 memset(&me.sa4, 0, sizeof(me.sa4));
295 me.sa4.sin_family = AF_INET;
296 me.sa4.sin_port = htons(connectport);
297 me.sa4.sin_addr.s_addr = INADDR_ANY;
298 Curl_inet_pton(AF_INET, connectaddr, &me.sa4.sin_addr);
299
300 rc = connect(sock, &me.sa, sizeof(me.sa4));
301
302 if(rc) {
303 int error = SOCKERRNO;
304 logmsg("Error connecting to %s:%hu: (%d) %s",
305 connectaddr, connectport, error, sstrerror(error));
306 return CURL_SOCKET_BAD;
307 }
308 logmsg("Connected fine to %s:%d", connectaddr, connectport);
309 return sock;
310 }
311
socks4(curl_socket_t fd,unsigned char * buffer,ssize_t rc)312 static curl_socket_t socks4(curl_socket_t fd,
313 unsigned char *buffer,
314 ssize_t rc)
315 {
316 unsigned char response[256 + 16];
317 curl_socket_t connfd;
318 unsigned char cd;
319 unsigned short s4port;
320
321 if(buffer[SOCKS4_CD] != 1) {
322 logmsg("SOCKS4 CD is not 1: %d", buffer[SOCKS4_CD]);
323 return CURL_SOCKET_BAD;
324 }
325 if(rc < 9) {
326 logmsg("SOCKS4 connect message too short: %zd", rc);
327 return CURL_SOCKET_BAD;
328 }
329 if(!config.port)
330 s4port = (unsigned short)((buffer[SOCKS4_DSTPORT]<<8) |
331 (buffer[SOCKS4_DSTPORT + 1]));
332 else
333 s4port = config.port;
334
335 connfd = socksconnect(s4port, config.addr);
336 if(connfd == CURL_SOCKET_BAD) {
337 /* failed */
338 cd = 91;
339 }
340 else {
341 /* success */
342 cd = 90;
343 }
344 response[0] = 0; /* reply version 0 */
345 response[1] = cd; /* result */
346 /* copy port and address from connect request */
347 memcpy(&response[2], &buffer[SOCKS4_DSTPORT], 6);
348 rc = (send)(fd, (char *)response, 8, 0);
349 if(rc != 8) {
350 logmsg("Sending SOCKS4 response failed!");
351 return CURL_SOCKET_BAD;
352 }
353 logmsg("Sent %zd bytes", rc);
354 loghex(response, rc);
355
356 if(cd == 90)
357 /* now do the transfer */
358 return connfd;
359
360 if(connfd != CURL_SOCKET_BAD)
361 sclose(connfd);
362
363 return CURL_SOCKET_BAD;
364 }
365
sockit(curl_socket_t fd)366 static curl_socket_t sockit(curl_socket_t fd)
367 {
368 unsigned char buffer[2*256 + 16];
369 unsigned char response[2*256 + 16];
370 ssize_t rc;
371 unsigned char len;
372 unsigned char type;
373 unsigned char rep = 0;
374 unsigned char *address;
375 unsigned short socksport;
376 curl_socket_t connfd = CURL_SOCKET_BAD;
377 unsigned short s5port;
378
379 getconfig();
380
381 rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
382 if(rc <= 0) {
383 logmsg("SOCKS identifier message missing, recv returned %zd", rc);
384 return CURL_SOCKET_BAD;
385 }
386
387 logmsg("READ %zd bytes", rc);
388 loghex(buffer, rc);
389
390 if(buffer[SOCKS5_VERSION] == 4)
391 return socks4(fd, buffer, rc);
392
393 if(rc < 3) {
394 logmsg("SOCKS5 identifier message too short: %zd", rc);
395 return CURL_SOCKET_BAD;
396 }
397
398 if(buffer[SOCKS5_VERSION] != config.version) {
399 logmsg("VERSION byte not %d", config.version);
400 return CURL_SOCKET_BAD;
401 }
402 if((buffer[SOCKS5_NMETHODS] < config.nmethods_min) ||
403 (buffer[SOCKS5_NMETHODS] > config.nmethods_max)) {
404 logmsg("NMETHODS byte not within %d - %d ",
405 config.nmethods_min, config.nmethods_max);
406 return CURL_SOCKET_BAD;
407 }
408 /* after NMETHODS follows that many bytes listing the methods the client
409 says it supports */
410 if(rc != (buffer[SOCKS5_NMETHODS] + 2)) {
411 logmsg("Expected %d bytes, got %zd", buffer[SOCKS5_NMETHODS] + 2, rc);
412 return CURL_SOCKET_BAD;
413 }
414 logmsg("Incoming request deemed fine!");
415
416 /* respond with two bytes: VERSION + METHOD */
417 response[0] = config.responseversion;
418 response[1] = config.responsemethod;
419 rc = (send)(fd, (char *)response, 2, 0);
420 if(rc != 2) {
421 logmsg("Sending response failed!");
422 return CURL_SOCKET_BAD;
423 }
424 logmsg("Sent %zd bytes", rc);
425 loghex(response, rc);
426
427 /* expect the request or auth */
428 rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
429 if(rc <= 0) {
430 logmsg("SOCKS5 request or auth message missing, recv returned %zd", rc);
431 return CURL_SOCKET_BAD;
432 }
433
434 logmsg("READ %zd bytes", rc);
435 loghex(buffer, rc);
436
437 if(config.responsemethod == 2) {
438 /* RFC 1929 authentication
439 +----+------+----------+------+----------+
440 |VER | ULEN | UNAME | PLEN | PASSWD |
441 +----+------+----------+------+----------+
442 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
443 +----+------+----------+------+----------+
444 */
445 unsigned char ulen;
446 unsigned char plen;
447 bool login = TRUE;
448 if(rc < 5) {
449 logmsg("Too short auth input: %zd", rc);
450 return CURL_SOCKET_BAD;
451 }
452 if(buffer[SOCKS5_VERSION] != 1) {
453 logmsg("Auth VERSION byte not 1, got %d", buffer[SOCKS5_VERSION]);
454 return CURL_SOCKET_BAD;
455 }
456 ulen = buffer[SOCKS5_ULEN];
457 if(rc < 4 + ulen) {
458 logmsg("Too short packet for username: %zd", rc);
459 return CURL_SOCKET_BAD;
460 }
461 plen = buffer[SOCKS5_ULEN + ulen + 1];
462 if(rc < 3 + ulen + plen) {
463 logmsg("Too short packet for ulen %d plen %d: %zd", ulen, plen, rc);
464 return CURL_SOCKET_BAD;
465 }
466 if((ulen != strlen(config.user)) ||
467 (plen != strlen(config.password)) ||
468 memcmp(&buffer[SOCKS5_UNAME], config.user, ulen) ||
469 memcmp(&buffer[SOCKS5_UNAME + ulen + 1], config.password, plen)) {
470 /* no match! */
471 logmsg("mismatched credentials!");
472 login = FALSE;
473 }
474 response[0] = 1;
475 response[1] = login ? 0 : 1;
476 rc = (send)(fd, (char *)response, 2, 0);
477 if(rc != 2) {
478 logmsg("Sending auth response failed!");
479 return CURL_SOCKET_BAD;
480 }
481 logmsg("Sent %zd bytes", rc);
482 loghex(response, rc);
483 if(!login)
484 return CURL_SOCKET_BAD;
485
486 /* expect the request */
487 rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
488 if(rc <= 0) {
489 logmsg("SOCKS5 request message missing, recv returned %zd", rc);
490 return CURL_SOCKET_BAD;
491 }
492
493 logmsg("READ %zd bytes", rc);
494 loghex(buffer, rc);
495 }
496 if(rc < 6) {
497 logmsg("Too short for request: %zd", rc);
498 return CURL_SOCKET_BAD;
499 }
500
501 if(buffer[SOCKS5_VERSION] != config.version) {
502 logmsg("Request VERSION byte not %d", config.version);
503 return CURL_SOCKET_BAD;
504 }
505 /* 1 == CONNECT */
506 if(buffer[SOCKS5_REQCMD] != config.reqcmd) {
507 logmsg("Request COMMAND byte not %d", config.reqcmd);
508 return CURL_SOCKET_BAD;
509 }
510 /* reserved, should be zero */
511 if(buffer[SOCKS5_RESERVED]) {
512 logmsg("Request COMMAND byte not %d", config.reqcmd);
513 return CURL_SOCKET_BAD;
514 }
515 /* ATYP:
516 o IP V4 address: X'01'
517 o DOMAINNAME: X'03'
518 o IP V6 address: X'04'
519 */
520 type = buffer[SOCKS5_ATYP];
521 address = &buffer[SOCKS5_DSTADDR];
522 switch(type) {
523 case 1:
524 /* 4 bytes IPv4 address */
525 len = 4;
526 break;
527 case 3:
528 /* The first octet of the address field contains the number of octets of
529 name that follow */
530 len = buffer[SOCKS5_DSTADDR];
531 len++;
532 break;
533 case 4:
534 /* 16 bytes IPv6 address */
535 len = 16;
536 break;
537 default:
538 logmsg("Unknown ATYP %d", type);
539 return CURL_SOCKET_BAD;
540 }
541 if(rc < (4 + len + 2)) {
542 logmsg("Request too short: %zd, expected %d", rc, 4 + len + 2);
543 return CURL_SOCKET_BAD;
544 }
545 logmsg("Received ATYP %d", type);
546
547 {
548 FILE *dump;
549 dump = fopen(reqlogfile, "ab");
550 if(dump) {
551 int i;
552 fprintf(dump, "atyp %u =>", type);
553 switch(type) {
554 case 1:
555 /* 4 bytes IPv4 address */
556 fprintf(dump, " %u.%u.%u.%u\n",
557 address[0], address[1], address[2], address[3]);
558 break;
559 case 3:
560 /* The first octet of the address field contains the number of octets
561 of name that follow */
562 fprintf(dump, " %.*s\n", len-1, &address[1]);
563 break;
564 case 4:
565 /* 16 bytes IPv6 address */
566 for(i = 0; i < 16; i++) {
567 fprintf(dump, " %02x", address[i]);
568 }
569 fprintf(dump, "\n");
570 break;
571 }
572 fclose(dump);
573 }
574 }
575
576 if(!config.port) {
577 unsigned char *portp = &buffer[SOCKS5_DSTADDR + len];
578 s5port = (unsigned short)((portp[0]<<8) | (portp[1]));
579 }
580 else
581 s5port = config.port;
582
583 if(!config.connectrep)
584 connfd = socksconnect(s5port, config.addr);
585
586 if(connfd == CURL_SOCKET_BAD) {
587 /* failed */
588 rep = 1;
589 }
590 else {
591 rep = config.connectrep;
592 }
593
594 /* */
595 response[SOCKS5_VERSION] = config.responseversion;
596
597 /*
598 o REP Reply field:
599 o X'00' succeeded
600 o X'01' general SOCKS server failure
601 o X'02' connection not allowed by ruleset
602 o X'03' Network unreachable
603 o X'04' Host unreachable
604 o X'05' Connection refused
605 o X'06' TTL expired
606 o X'07' Command not supported
607 o X'08' Address type not supported
608 o X'09' to X'FF' unassigned
609 */
610 response[SOCKS5_REP] = rep;
611 response[SOCKS5_RESERVED] = 0; /* must be zero */
612 response[SOCKS5_ATYP] = type; /* address type */
613
614 /* mirror back the original addr + port */
615
616 /* address or hostname */
617 memcpy(&response[SOCKS5_BNDADDR], address, len);
618
619 /* port number */
620 memcpy(&response[SOCKS5_BNDADDR + len],
621 &buffer[SOCKS5_DSTADDR + len], sizeof(socksport));
622
623 rc = (send)(fd, (char *)response, (size_t)(len + 6), 0);
624 if(rc != (len + 6)) {
625 logmsg("Sending connect response failed!");
626 return CURL_SOCKET_BAD;
627 }
628 logmsg("Sent %zd bytes", rc);
629 loghex(response, rc);
630
631 if(!rep)
632 return connfd;
633
634 if(connfd != CURL_SOCKET_BAD)
635 sclose(connfd);
636
637 return CURL_SOCKET_BAD;
638 }
639
640 struct perclient {
641 size_t fromremote;
642 size_t fromclient;
643 curl_socket_t remotefd;
644 curl_socket_t clientfd;
645 bool used;
646 };
647
648 /* return non-zero when transfer is done */
tunnel(struct perclient * cp,fd_set * fds)649 static int tunnel(struct perclient *cp, fd_set *fds)
650 {
651 ssize_t nread;
652 ssize_t nwrite;
653 char buffer[512];
654 if(FD_ISSET(cp->clientfd, fds)) {
655 /* read from client, send to remote */
656 nread = recv(cp->clientfd, buffer, sizeof(buffer), 0);
657 if(nread > 0) {
658 nwrite = send(cp->remotefd, (char *)buffer,
659 (SEND_TYPE_ARG3)nread, 0);
660 if(nwrite != nread)
661 return 1;
662 cp->fromclient += nwrite;
663 }
664 else
665 return 1;
666 }
667 if(FD_ISSET(cp->remotefd, fds)) {
668 /* read from remote, send to client */
669 nread = recv(cp->remotefd, buffer, sizeof(buffer), 0);
670 if(nread > 0) {
671 nwrite = send(cp->clientfd, (char *)buffer,
672 (SEND_TYPE_ARG3)nread, 0);
673 if(nwrite != nread)
674 return 1;
675 cp->fromremote += nwrite;
676 }
677 else
678 return 1;
679 }
680 return 0;
681 }
682
683 /*
684 sockfdp is a pointer to an established stream or CURL_SOCKET_BAD
685
686 if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must
687 accept()
688 */
incoming(curl_socket_t listenfd)689 static bool incoming(curl_socket_t listenfd)
690 {
691 fd_set fds_read;
692 fd_set fds_write;
693 fd_set fds_err;
694 int clients = 0; /* connected clients */
695 struct perclient c[2];
696
697 memset(c, 0, sizeof(c));
698 if(got_exit_signal) {
699 logmsg("signalled to die, exiting...");
700 return FALSE;
701 }
702
703 #ifdef HAVE_GETPPID
704 /* As a last resort, quit if socks5 process becomes orphan. */
705 if(getppid() <= 1) {
706 logmsg("process becomes orphan, exiting");
707 return FALSE;
708 }
709 #endif
710
711 do {
712 int i;
713 ssize_t rc;
714 int error = 0;
715 curl_socket_t sockfd = listenfd;
716 int maxfd = (int)sockfd;
717
718 FD_ZERO(&fds_read);
719 FD_ZERO(&fds_write);
720 FD_ZERO(&fds_err);
721
722 /* there's always a socket to wait for */
723 FD_SET(sockfd, &fds_read);
724
725 for(i = 0; i < 2; i++) {
726 if(c[i].used) {
727 curl_socket_t fd = c[i].clientfd;
728 FD_SET(fd, &fds_read);
729 if((int)fd > maxfd)
730 maxfd = (int)fd;
731 fd = c[i].remotefd;
732 FD_SET(fd, &fds_read);
733 if((int)fd > maxfd)
734 maxfd = (int)fd;
735 }
736 }
737
738 do {
739 /* select() blocking behavior call on blocking descriptors please */
740 rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, NULL);
741 if(got_exit_signal) {
742 logmsg("signalled to die, exiting...");
743 return FALSE;
744 }
745 } while((rc == -1) && ((error = errno) == EINTR));
746
747 if(rc < 0) {
748 logmsg("select() failed with error: (%d) %s",
749 error, strerror(error));
750 return FALSE;
751 }
752
753 if((clients < 2) && FD_ISSET(sockfd, &fds_read)) {
754 curl_socket_t newfd = accept(sockfd, NULL, NULL);
755 if(CURL_SOCKET_BAD == newfd) {
756 error = SOCKERRNO;
757 logmsg("accept(%" CURL_FORMAT_SOCKET_T ", NULL, NULL) "
758 "failed with error: (%d) %s",
759 sockfd, error, sstrerror(error));
760 }
761 else {
762 curl_socket_t remotefd;
763 logmsg("====> Client connect, fd %" CURL_FORMAT_SOCKET_T ". "
764 "Read config from %s", newfd, configfile);
765 remotefd = sockit(newfd); /* SOCKS until done */
766 if(remotefd == CURL_SOCKET_BAD) {
767 logmsg("====> Client disconnect");
768 sclose(newfd);
769 }
770 else {
771 struct perclient *cp = &c[0];
772 logmsg("====> Tunnel transfer");
773
774 if(c[0].used)
775 cp = &c[1];
776 cp->fromremote = 0;
777 cp->fromclient = 0;
778 cp->clientfd = newfd;
779 cp->remotefd = remotefd;
780 cp->used = TRUE;
781 clients++;
782 }
783
784 }
785 }
786 for(i = 0; i < 2; i++) {
787 struct perclient *cp = &c[i];
788 if(cp->used) {
789 if(tunnel(cp, &fds_read)) {
790 logmsg("SOCKS transfer completed. Bytes: < %zu > %zu",
791 cp->fromremote, cp->fromclient);
792 sclose(cp->clientfd);
793 sclose(cp->remotefd);
794 cp->used = FALSE;
795 clients--;
796 }
797 }
798 }
799 } while(clients);
800
801 return TRUE;
802 }
803
sockdaemon(curl_socket_t sock,unsigned short * listenport,const char * unix_socket)804 static curl_socket_t sockdaemon(curl_socket_t sock,
805 unsigned short *listenport
806 #ifdef USE_UNIX_SOCKETS
807 , const char *unix_socket
808 #endif
809 )
810 {
811 /* passive daemon style */
812 srvr_sockaddr_union_t listener;
813 int flag;
814 int rc;
815 int totdelay = 0;
816 int maxretr = 10;
817 int delay = 20;
818 int attempt = 0;
819 int error = 0;
820
821 do {
822 attempt++;
823 flag = 1;
824 rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
825 (void *)&flag, sizeof(flag));
826 if(rc) {
827 error = SOCKERRNO;
828 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
829 error, sstrerror(error));
830 if(maxretr) {
831 rc = wait_ms(delay);
832 if(rc) {
833 /* should not happen */
834 error = errno;
835 logmsg("wait_ms() failed with error: (%d) %s",
836 error, strerror(error));
837 sclose(sock);
838 return CURL_SOCKET_BAD;
839 }
840 if(got_exit_signal) {
841 logmsg("signalled to die, exiting...");
842 sclose(sock);
843 return CURL_SOCKET_BAD;
844 }
845 totdelay += delay;
846 delay *= 2; /* double the sleep for next attempt */
847 }
848 }
849 } while(rc && maxretr--);
850
851 if(rc) {
852 logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s",
853 attempt, totdelay, error, strerror(error));
854 logmsg("Continuing anyway...");
855 }
856
857 /* When the specified listener port is zero, it is actually a
858 request to let the system choose a non-zero available port. */
859
860 switch(socket_domain) {
861 case AF_INET:
862 memset(&listener.sa4, 0, sizeof(listener.sa4));
863 listener.sa4.sin_family = AF_INET;
864 listener.sa4.sin_addr.s_addr = INADDR_ANY;
865 listener.sa4.sin_port = htons(*listenport);
866 rc = bind(sock, &listener.sa, sizeof(listener.sa4));
867 break;
868 #ifdef USE_IPV6
869 case AF_INET6:
870 memset(&listener.sa6, 0, sizeof(listener.sa6));
871 listener.sa6.sin6_family = AF_INET6;
872 listener.sa6.sin6_addr = in6addr_any;
873 listener.sa6.sin6_port = htons(*listenport);
874 rc = bind(sock, &listener.sa, sizeof(listener.sa6));
875 break;
876 #endif /* USE_IPV6 */
877 #ifdef USE_UNIX_SOCKETS
878 case AF_UNIX:
879 rc = bind_unix_socket(sock, unix_socket, &listener.sau);
880 #endif
881 }
882
883 if(rc) {
884 error = SOCKERRNO;
885 #ifdef USE_UNIX_SOCKETS
886 if(socket_domain == AF_UNIX)
887 logmsg("Error binding socket on path %s: (%d) %s",
888 unix_socket, error, sstrerror(error));
889 else
890 #endif
891 logmsg("Error binding socket on port %hu: (%d) %s",
892 *listenport, error, sstrerror(error));
893 sclose(sock);
894 return CURL_SOCKET_BAD;
895 }
896
897 if(!*listenport
898 #ifdef USE_UNIX_SOCKETS
899 && !unix_socket
900 #endif
901 ) {
902 /* The system was supposed to choose a port number, figure out which
903 port we actually got and update the listener port value with it. */
904 curl_socklen_t la_size;
905 srvr_sockaddr_union_t localaddr;
906 #ifdef USE_IPV6
907 if(socket_domain == AF_INET6)
908 la_size = sizeof(localaddr.sa6);
909 else
910 #endif
911 la_size = sizeof(localaddr.sa4);
912 memset(&localaddr.sa, 0, (size_t)la_size);
913 if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
914 error = SOCKERRNO;
915 logmsg("getsockname() failed with error: (%d) %s",
916 error, sstrerror(error));
917 sclose(sock);
918 return CURL_SOCKET_BAD;
919 }
920 switch(localaddr.sa.sa_family) {
921 case AF_INET:
922 *listenport = ntohs(localaddr.sa4.sin_port);
923 break;
924 #ifdef USE_IPV6
925 case AF_INET6:
926 *listenport = ntohs(localaddr.sa6.sin6_port);
927 break;
928 #endif
929 default:
930 break;
931 }
932 if(!*listenport) {
933 /* Real failure, listener port shall not be zero beyond this point. */
934 logmsg("Apparently getsockname() succeeded, with listener port zero.");
935 logmsg("A valid reason for this failure is a binary built without");
936 logmsg("proper network library linkage. This might not be the only");
937 logmsg("reason, but double check it before anything else.");
938 sclose(sock);
939 return CURL_SOCKET_BAD;
940 }
941 }
942
943 /* start accepting connections */
944 rc = listen(sock, 5);
945 if(0 != rc) {
946 error = SOCKERRNO;
947 logmsg("listen(%" CURL_FORMAT_SOCKET_T ", 5) failed with error: (%d) %s",
948 sock, error, sstrerror(error));
949 sclose(sock);
950 return CURL_SOCKET_BAD;
951 }
952
953 return sock;
954 }
955
956
main(int argc,char * argv[])957 int main(int argc, char *argv[])
958 {
959 curl_socket_t sock = CURL_SOCKET_BAD;
960 curl_socket_t msgsock = CURL_SOCKET_BAD;
961 int wrotepidfile = 0;
962 int wroteportfile = 0;
963 const char *pidname = ".socksd.pid";
964 const char *portname = NULL; /* none by default */
965 bool juggle_again;
966 int error;
967 int arg = 1;
968
969 #ifdef USE_UNIX_SOCKETS
970 const char *unix_socket = NULL;
971 bool unlink_socket = false;
972 #endif
973
974 while(argc>arg) {
975 if(!strcmp("--version", argv[arg])) {
976 printf("socksd IPv4%s\n",
977 #ifdef USE_IPV6
978 "/IPv6"
979 #else
980 ""
981 #endif
982 );
983 return 0;
984 }
985 else if(!strcmp("--pidfile", argv[arg])) {
986 arg++;
987 if(argc>arg)
988 pidname = argv[arg++];
989 }
990 else if(!strcmp("--portfile", argv[arg])) {
991 arg++;
992 if(argc>arg)
993 portname = argv[arg++];
994 }
995 else if(!strcmp("--config", argv[arg])) {
996 arg++;
997 if(argc>arg)
998 configfile = argv[arg++];
999 }
1000 else if(!strcmp("--backend", argv[arg])) {
1001 arg++;
1002 if(argc>arg)
1003 backendaddr = argv[arg++];
1004 }
1005 else if(!strcmp("--backendport", argv[arg])) {
1006 arg++;
1007 if(argc>arg)
1008 backendport = (unsigned short)atoi(argv[arg++]);
1009 }
1010 else if(!strcmp("--logfile", argv[arg])) {
1011 arg++;
1012 if(argc>arg)
1013 serverlogfile = argv[arg++];
1014 }
1015 else if(!strcmp("--reqfile", argv[arg])) {
1016 arg++;
1017 if(argc>arg)
1018 reqlogfile = argv[arg++];
1019 }
1020 else if(!strcmp("--ipv6", argv[arg])) {
1021 #ifdef USE_IPV6
1022 socket_domain = AF_INET6;
1023 socket_type = "IPv6";
1024 #endif
1025 arg++;
1026 }
1027 else if(!strcmp("--ipv4", argv[arg])) {
1028 /* for completeness, we support this option as well */
1029 #ifdef USE_IPV6
1030 socket_type = "IPv4";
1031 #endif
1032 arg++;
1033 }
1034 else if(!strcmp("--unix-socket", argv[arg])) {
1035 arg++;
1036 if(argc>arg) {
1037 #ifdef USE_UNIX_SOCKETS
1038 struct sockaddr_un sau;
1039 unix_socket = argv[arg];
1040 if(strlen(unix_socket) >= sizeof(sau.sun_path)) {
1041 fprintf(stderr,
1042 "socksd: socket path must be shorter than %zu chars: %s\n",
1043 sizeof(sau.sun_path), unix_socket);
1044 return 0;
1045 }
1046 socket_domain = AF_UNIX;
1047 socket_type = "unix";
1048 #endif
1049 arg++;
1050 }
1051 }
1052 else if(!strcmp("--port", argv[arg])) {
1053 arg++;
1054 if(argc>arg) {
1055 char *endptr;
1056 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1057 port = curlx_ultous(ulnum);
1058 arg++;
1059 }
1060 }
1061 else {
1062 puts("Usage: socksd [option]\n"
1063 " --backend [ipv4 addr]\n"
1064 " --backendport [TCP port]\n"
1065 " --config [file]\n"
1066 " --version\n"
1067 " --logfile [file]\n"
1068 " --pidfile [file]\n"
1069 " --portfile [file]\n"
1070 " --reqfile [file]\n"
1071 " --ipv4\n"
1072 " --ipv6\n"
1073 " --unix-socket [file]\n"
1074 " --bindonly\n"
1075 " --port [port]\n");
1076 return 0;
1077 }
1078 }
1079
1080 #ifdef _WIN32
1081 win32_init();
1082 atexit(win32_cleanup);
1083
1084 setmode(fileno(stdin), O_BINARY);
1085 setmode(fileno(stdout), O_BINARY);
1086 setmode(fileno(stderr), O_BINARY);
1087 #endif
1088
1089 install_signal_handlers(false);
1090
1091 sock = socket(socket_domain, SOCK_STREAM, 0);
1092
1093 if(CURL_SOCKET_BAD == sock) {
1094 error = SOCKERRNO;
1095 logmsg("Error creating socket: (%d) %s",
1096 error, sstrerror(error));
1097 goto socks5_cleanup;
1098 }
1099
1100 {
1101 /* passive daemon style */
1102 sock = sockdaemon(sock, &port
1103 #ifdef USE_UNIX_SOCKETS
1104 , unix_socket
1105 #endif
1106 );
1107 if(CURL_SOCKET_BAD == sock) {
1108 goto socks5_cleanup;
1109 }
1110 #ifdef USE_UNIX_SOCKETS
1111 unlink_socket = true;
1112 #endif
1113 msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
1114 }
1115
1116 logmsg("Running %s version", socket_type);
1117
1118 #ifdef USE_UNIX_SOCKETS
1119 if(socket_domain == AF_UNIX)
1120 logmsg("Listening on unix socket %s", unix_socket);
1121 else
1122 #endif
1123 logmsg("Listening on port %hu", port);
1124
1125 wrotepidfile = write_pidfile(pidname);
1126 if(!wrotepidfile) {
1127 goto socks5_cleanup;
1128 }
1129
1130 if(portname) {
1131 wroteportfile = write_portfile(portname, port);
1132 if(!wroteportfile) {
1133 goto socks5_cleanup;
1134 }
1135 }
1136
1137 do {
1138 juggle_again = incoming(sock);
1139 } while(juggle_again);
1140
1141 socks5_cleanup:
1142
1143 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
1144 sclose(msgsock);
1145
1146 if(sock != CURL_SOCKET_BAD)
1147 sclose(sock);
1148
1149 #ifdef USE_UNIX_SOCKETS
1150 if(unlink_socket && socket_domain == AF_UNIX) {
1151 error = unlink(unix_socket);
1152 logmsg("unlink(%s) = %d (%s)", unix_socket, error, strerror(error));
1153 }
1154 #endif
1155
1156 if(wrotepidfile)
1157 unlink(pidname);
1158 if(wroteportfile)
1159 unlink(portname);
1160
1161 restore_signal_handlers(false);
1162
1163 if(got_exit_signal) {
1164 logmsg("============> socksd exits with signal (%d)", exit_signal);
1165 /*
1166 * To properly set the return status of the process we
1167 * must raise the same signal SIGINT or SIGTERM that we
1168 * caught and let the old handler take care of it.
1169 */
1170 raise(exit_signal);
1171 }
1172
1173 logmsg("============> socksd quits");
1174 return 0;
1175 }
1176