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