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