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