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