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