xref: /curl/tests/server/socksd.c (revision fbf5d507)
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