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