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