xref: /curl/tests/server/socksd.c (revision e411c98f)
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 USE_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[260];
197       if(2 == sscanf(buffer, "%31s %259s", 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: %zd", 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 %zd 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[2*256 + 16];
369   unsigned char response[2*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   if(rc <= 0) {
383     logmsg("SOCKS identifier message missing, recv returned %zd", rc);
384     return CURL_SOCKET_BAD;
385   }
386 
387   logmsg("READ %zd bytes", rc);
388   loghex(buffer, rc);
389 
390   if(buffer[SOCKS5_VERSION] == 4)
391     return socks4(fd, buffer, rc);
392 
393   if(rc < 3) {
394     logmsg("SOCKS5 identifier message too short: %zd", rc);
395     return CURL_SOCKET_BAD;
396   }
397 
398   if(buffer[SOCKS5_VERSION] != config.version) {
399     logmsg("VERSION byte not %d", config.version);
400     return CURL_SOCKET_BAD;
401   }
402   if((buffer[SOCKS5_NMETHODS] < config.nmethods_min) ||
403      (buffer[SOCKS5_NMETHODS] > config.nmethods_max)) {
404     logmsg("NMETHODS byte not within %d - %d ",
405            config.nmethods_min, config.nmethods_max);
406     return CURL_SOCKET_BAD;
407   }
408   /* after NMETHODS follows that many bytes listing the methods the client
409      says it supports */
410   if(rc != (buffer[SOCKS5_NMETHODS] + 2)) {
411     logmsg("Expected %d bytes, got %zd", buffer[SOCKS5_NMETHODS] + 2, rc);
412     return CURL_SOCKET_BAD;
413   }
414   logmsg("Incoming request deemed fine!");
415 
416   /* respond with two bytes: VERSION + METHOD */
417   response[0] = config.responseversion;
418   response[1] = config.responsemethod;
419   rc = (send)(fd, (char *)response, 2, 0);
420   if(rc != 2) {
421     logmsg("Sending response failed!");
422     return CURL_SOCKET_BAD;
423   }
424   logmsg("Sent %zd bytes", rc);
425   loghex(response, rc);
426 
427   /* expect the request or auth */
428   rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
429   if(rc <= 0) {
430     logmsg("SOCKS5 request or auth message missing, recv returned %zd", rc);
431     return CURL_SOCKET_BAD;
432   }
433 
434   logmsg("READ %zd bytes", rc);
435   loghex(buffer, rc);
436 
437   if(config.responsemethod == 2) {
438     /* RFC 1929 authentication
439        +----+------+----------+------+----------+
440        |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
441        +----+------+----------+------+----------+
442        | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
443        +----+------+----------+------+----------+
444     */
445     unsigned char ulen;
446     unsigned char plen;
447     bool login = TRUE;
448     if(rc < 5) {
449       logmsg("Too short auth input: %zd", rc);
450       return CURL_SOCKET_BAD;
451     }
452     if(buffer[SOCKS5_VERSION] != 1) {
453       logmsg("Auth VERSION byte not 1, got %d", buffer[SOCKS5_VERSION]);
454       return CURL_SOCKET_BAD;
455     }
456     ulen = buffer[SOCKS5_ULEN];
457     if(rc < 4 + ulen) {
458       logmsg("Too short packet for username: %zd", rc);
459       return CURL_SOCKET_BAD;
460     }
461     plen = buffer[SOCKS5_ULEN + ulen + 1];
462     if(rc < 3 + ulen + plen) {
463       logmsg("Too short packet for ulen %d plen %d: %zd", ulen, plen, rc);
464       return CURL_SOCKET_BAD;
465     }
466     if((ulen != strlen(config.user)) ||
467        (plen != strlen(config.password)) ||
468        memcmp(&buffer[SOCKS5_UNAME], config.user, ulen) ||
469        memcmp(&buffer[SOCKS5_UNAME + ulen + 1], config.password, plen)) {
470       /* no match! */
471       logmsg("mismatched credentials!");
472       login = FALSE;
473     }
474     response[0] = 1;
475     response[1] = login ? 0 : 1;
476     rc = (send)(fd, (char *)response, 2, 0);
477     if(rc != 2) {
478       logmsg("Sending auth response failed!");
479       return CURL_SOCKET_BAD;
480     }
481     logmsg("Sent %zd bytes", rc);
482     loghex(response, rc);
483     if(!login)
484       return CURL_SOCKET_BAD;
485 
486     /* expect the request */
487     rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
488     if(rc <= 0) {
489       logmsg("SOCKS5 request message missing, recv returned %zd", rc);
490       return CURL_SOCKET_BAD;
491     }
492 
493     logmsg("READ %zd bytes", rc);
494     loghex(buffer, rc);
495   }
496   if(rc < 6) {
497     logmsg("Too short for request: %zd", rc);
498     return CURL_SOCKET_BAD;
499   }
500 
501   if(buffer[SOCKS5_VERSION] != config.version) {
502     logmsg("Request VERSION byte not %d", config.version);
503     return CURL_SOCKET_BAD;
504   }
505   /* 1 == CONNECT */
506   if(buffer[SOCKS5_REQCMD] != config.reqcmd) {
507     logmsg("Request COMMAND byte not %d", config.reqcmd);
508     return CURL_SOCKET_BAD;
509   }
510   /* reserved, should be zero */
511   if(buffer[SOCKS5_RESERVED]) {
512     logmsg("Request COMMAND byte not %d", config.reqcmd);
513     return CURL_SOCKET_BAD;
514   }
515   /* ATYP:
516      o  IP V4 address: X'01'
517      o  DOMAINNAME: X'03'
518      o  IP V6 address: X'04'
519   */
520   type = buffer[SOCKS5_ATYP];
521   address = &buffer[SOCKS5_DSTADDR];
522   switch(type) {
523   case 1:
524     /* 4 bytes IPv4 address */
525     len = 4;
526     break;
527   case 3:
528     /* The first octet of the address field contains the number of octets of
529        name that follow */
530     len = buffer[SOCKS5_DSTADDR];
531     len++;
532     break;
533   case 4:
534     /* 16 bytes IPv6 address */
535     len = 16;
536     break;
537   default:
538     logmsg("Unknown ATYP %d", type);
539     return CURL_SOCKET_BAD;
540   }
541   if(rc < (4 + len + 2)) {
542     logmsg("Request too short: %zd, expected %d", rc, 4 + len + 2);
543     return CURL_SOCKET_BAD;
544   }
545   logmsg("Received ATYP %d", type);
546 
547   {
548     FILE *dump;
549     dump = fopen(reqlogfile, "ab");
550     if(dump) {
551       int i;
552       fprintf(dump, "atyp %u =>", type);
553       switch(type) {
554       case 1:
555         /* 4 bytes IPv4 address */
556         fprintf(dump, " %u.%u.%u.%u\n",
557                 address[0], address[1], address[2], address[3]);
558         break;
559       case 3:
560         /* The first octet of the address field contains the number of octets
561            of name that follow */
562         fprintf(dump, " %.*s\n", len-1, &address[1]);
563         break;
564       case 4:
565         /* 16 bytes IPv6 address */
566         for(i = 0; i < 16; i++) {
567           fprintf(dump, " %02x", address[i]);
568         }
569         fprintf(dump, "\n");
570         break;
571       }
572       fclose(dump);
573     }
574   }
575 
576   if(!config.port) {
577     unsigned char *portp = &buffer[SOCKS5_DSTADDR + len];
578     s5port = (unsigned short)((portp[0]<<8) | (portp[1]));
579   }
580   else
581     s5port = config.port;
582 
583   if(!config.connectrep)
584     connfd = socksconnect(s5port, config.addr);
585 
586   if(connfd == CURL_SOCKET_BAD) {
587     /* failed */
588     rep = 1;
589   }
590   else {
591     rep = config.connectrep;
592   }
593 
594   /* */
595   response[SOCKS5_VERSION] = config.responseversion;
596 
597   /*
598     o  REP    Reply field:
599     o  X'00' succeeded
600     o  X'01' general SOCKS server failure
601     o  X'02' connection not allowed by ruleset
602     o  X'03' Network unreachable
603     o  X'04' Host unreachable
604     o  X'05' Connection refused
605     o  X'06' TTL expired
606     o  X'07' Command not supported
607     o  X'08' Address type not supported
608     o  X'09' to X'FF' unassigned
609   */
610   response[SOCKS5_REP] = rep;
611   response[SOCKS5_RESERVED] = 0; /* must be zero */
612   response[SOCKS5_ATYP] = type; /* address type */
613 
614   /* mirror back the original addr + port */
615 
616   /* address or hostname */
617   memcpy(&response[SOCKS5_BNDADDR], address, len);
618 
619   /* port number */
620   memcpy(&response[SOCKS5_BNDADDR + len],
621          &buffer[SOCKS5_DSTADDR + len], sizeof(socksport));
622 
623   rc = (send)(fd, (char *)response, (size_t)(len + 6), 0);
624   if(rc != (len + 6)) {
625     logmsg("Sending connect response failed!");
626     return CURL_SOCKET_BAD;
627   }
628   logmsg("Sent %zd bytes", rc);
629   loghex(response, rc);
630 
631   if(!rep)
632     return connfd;
633 
634   if(connfd != CURL_SOCKET_BAD)
635     sclose(connfd);
636 
637   return CURL_SOCKET_BAD;
638 }
639 
640 struct perclient {
641   size_t fromremote;
642   size_t fromclient;
643   curl_socket_t remotefd;
644   curl_socket_t clientfd;
645   bool used;
646 };
647 
648 /* return non-zero when transfer is done */
tunnel(struct perclient * cp,fd_set * fds)649 static int tunnel(struct perclient *cp, fd_set *fds)
650 {
651   ssize_t nread;
652   ssize_t nwrite;
653   char buffer[512];
654   if(FD_ISSET(cp->clientfd, fds)) {
655     /* read from client, send to remote */
656     nread = recv(cp->clientfd, buffer, sizeof(buffer), 0);
657     if(nread > 0) {
658       nwrite = send(cp->remotefd, (char *)buffer,
659                     (SEND_TYPE_ARG3)nread, 0);
660       if(nwrite != nread)
661         return 1;
662       cp->fromclient += nwrite;
663     }
664     else
665       return 1;
666   }
667   if(FD_ISSET(cp->remotefd, fds)) {
668     /* read from remote, send to client */
669     nread = recv(cp->remotefd, buffer, sizeof(buffer), 0);
670     if(nread > 0) {
671       nwrite = send(cp->clientfd, (char *)buffer,
672                     (SEND_TYPE_ARG3)nread, 0);
673       if(nwrite != nread)
674         return 1;
675       cp->fromremote += nwrite;
676     }
677     else
678       return 1;
679   }
680   return 0;
681 }
682 
683 /*
684   sockfdp is a pointer to an established stream or CURL_SOCKET_BAD
685 
686   if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must
687   accept()
688 */
incoming(curl_socket_t listenfd)689 static bool incoming(curl_socket_t listenfd)
690 {
691   fd_set fds_read;
692   fd_set fds_write;
693   fd_set fds_err;
694   int clients = 0; /* connected clients */
695   struct perclient c[2];
696 
697   memset(c, 0, sizeof(c));
698   if(got_exit_signal) {
699     logmsg("signalled to die, exiting...");
700     return FALSE;
701   }
702 
703 #ifdef HAVE_GETPPID
704   /* As a last resort, quit if socks5 process becomes orphan. */
705   if(getppid() <= 1) {
706     logmsg("process becomes orphan, exiting");
707     return FALSE;
708   }
709 #endif
710 
711   do {
712     int i;
713     ssize_t rc;
714     int error = 0;
715     curl_socket_t sockfd = listenfd;
716     int maxfd = (int)sockfd;
717 
718     FD_ZERO(&fds_read);
719     FD_ZERO(&fds_write);
720     FD_ZERO(&fds_err);
721 
722     /* there's always a socket to wait for */
723     FD_SET(sockfd, &fds_read);
724 
725     for(i = 0; i < 2; i++) {
726       if(c[i].used) {
727         curl_socket_t fd = c[i].clientfd;
728         FD_SET(fd, &fds_read);
729         if((int)fd > maxfd)
730           maxfd = (int)fd;
731         fd = c[i].remotefd;
732         FD_SET(fd, &fds_read);
733         if((int)fd > maxfd)
734           maxfd = (int)fd;
735       }
736     }
737 
738     do {
739       /* select() blocking behavior call on blocking descriptors please */
740       rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, NULL);
741       if(got_exit_signal) {
742         logmsg("signalled to die, exiting...");
743         return FALSE;
744       }
745     } while((rc == -1) && ((error = errno) == EINTR));
746 
747     if(rc < 0) {
748       logmsg("select() failed with error: (%d) %s",
749              error, strerror(error));
750       return FALSE;
751     }
752 
753     if((clients < 2) && FD_ISSET(sockfd, &fds_read)) {
754       curl_socket_t newfd = accept(sockfd, NULL, NULL);
755       if(CURL_SOCKET_BAD == newfd) {
756         error = SOCKERRNO;
757         logmsg("accept(%" CURL_FORMAT_SOCKET_T ", NULL, NULL) "
758                "failed with error: (%d) %s",
759                sockfd, error, sstrerror(error));
760       }
761       else {
762         curl_socket_t remotefd;
763         logmsg("====> Client connect, fd %" CURL_FORMAT_SOCKET_T ". "
764                "Read config from %s", newfd, configfile);
765         remotefd = sockit(newfd); /* SOCKS until done */
766         if(remotefd == CURL_SOCKET_BAD) {
767           logmsg("====> Client disconnect");
768           sclose(newfd);
769         }
770         else {
771           struct perclient *cp = &c[0];
772           logmsg("====> Tunnel transfer");
773 
774           if(c[0].used)
775             cp = &c[1];
776           cp->fromremote = 0;
777           cp->fromclient = 0;
778           cp->clientfd = newfd;
779           cp->remotefd = remotefd;
780           cp->used = TRUE;
781           clients++;
782         }
783 
784       }
785     }
786     for(i = 0; i < 2; i++) {
787       struct perclient *cp = &c[i];
788       if(cp->used) {
789         if(tunnel(cp, &fds_read)) {
790           logmsg("SOCKS transfer completed. Bytes: < %zu > %zu",
791                  cp->fromremote, cp->fromclient);
792           sclose(cp->clientfd);
793           sclose(cp->remotefd);
794           cp->used = FALSE;
795           clients--;
796         }
797       }
798     }
799   } while(clients);
800 
801   return TRUE;
802 }
803 
sockdaemon(curl_socket_t sock,unsigned short * listenport,const char * unix_socket)804 static curl_socket_t sockdaemon(curl_socket_t sock,
805                                 unsigned short *listenport
806 #ifdef USE_UNIX_SOCKETS
807         , const char *unix_socket
808 #endif
809         )
810 {
811   /* passive daemon style */
812   srvr_sockaddr_union_t listener;
813   int flag;
814   int rc;
815   int totdelay = 0;
816   int maxretr = 10;
817   int delay = 20;
818   int attempt = 0;
819   int error = 0;
820 
821   do {
822     attempt++;
823     flag = 1;
824     rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
825          (void *)&flag, sizeof(flag));
826     if(rc) {
827       error = SOCKERRNO;
828       logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
829              error, sstrerror(error));
830       if(maxretr) {
831         rc = wait_ms(delay);
832         if(rc) {
833           /* should not happen */
834           error = errno;
835           logmsg("wait_ms() failed with error: (%d) %s",
836                  error, strerror(error));
837           sclose(sock);
838           return CURL_SOCKET_BAD;
839         }
840         if(got_exit_signal) {
841           logmsg("signalled to die, exiting...");
842           sclose(sock);
843           return CURL_SOCKET_BAD;
844         }
845         totdelay += delay;
846         delay *= 2; /* double the sleep for next attempt */
847       }
848     }
849   } while(rc && maxretr--);
850 
851   if(rc) {
852     logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s",
853            attempt, totdelay, error, strerror(error));
854     logmsg("Continuing anyway...");
855   }
856 
857   /* When the specified listener port is zero, it is actually a
858      request to let the system choose a non-zero available port. */
859 
860   switch(socket_domain) {
861     case AF_INET:
862       memset(&listener.sa4, 0, sizeof(listener.sa4));
863       listener.sa4.sin_family = AF_INET;
864       listener.sa4.sin_addr.s_addr = INADDR_ANY;
865       listener.sa4.sin_port = htons(*listenport);
866       rc = bind(sock, &listener.sa, sizeof(listener.sa4));
867       break;
868 #ifdef USE_IPV6
869     case AF_INET6:
870       memset(&listener.sa6, 0, sizeof(listener.sa6));
871       listener.sa6.sin6_family = AF_INET6;
872       listener.sa6.sin6_addr = in6addr_any;
873       listener.sa6.sin6_port = htons(*listenport);
874       rc = bind(sock, &listener.sa, sizeof(listener.sa6));
875       break;
876 #endif /* USE_IPV6 */
877 #ifdef USE_UNIX_SOCKETS
878     case AF_UNIX:
879     rc = bind_unix_socket(sock, unix_socket, &listener.sau);
880 #endif
881   }
882 
883   if(rc) {
884     error = SOCKERRNO;
885 #ifdef USE_UNIX_SOCKETS
886     if(socket_domain == AF_UNIX)
887       logmsg("Error binding socket on path %s: (%d) %s",
888              unix_socket, error, sstrerror(error));
889     else
890 #endif
891       logmsg("Error binding socket on port %hu: (%d) %s",
892              *listenport, error, sstrerror(error));
893     sclose(sock);
894     return CURL_SOCKET_BAD;
895   }
896 
897   if(!*listenport
898 #ifdef USE_UNIX_SOCKETS
899           && !unix_socket
900 #endif
901     ) {
902     /* The system was supposed to choose a port number, figure out which
903        port we actually got and update the listener port value with it. */
904     curl_socklen_t la_size;
905     srvr_sockaddr_union_t localaddr;
906 #ifdef USE_IPV6
907     if(socket_domain == AF_INET6)
908       la_size = sizeof(localaddr.sa6);
909     else
910 #endif
911       la_size = sizeof(localaddr.sa4);
912     memset(&localaddr.sa, 0, (size_t)la_size);
913     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
914       error = SOCKERRNO;
915       logmsg("getsockname() failed with error: (%d) %s",
916              error, sstrerror(error));
917       sclose(sock);
918       return CURL_SOCKET_BAD;
919     }
920     switch(localaddr.sa.sa_family) {
921     case AF_INET:
922       *listenport = ntohs(localaddr.sa4.sin_port);
923       break;
924 #ifdef USE_IPV6
925     case AF_INET6:
926       *listenport = ntohs(localaddr.sa6.sin6_port);
927       break;
928 #endif
929     default:
930       break;
931     }
932     if(!*listenport) {
933       /* Real failure, listener port shall not be zero beyond this point. */
934       logmsg("Apparently getsockname() succeeded, with listener port zero.");
935       logmsg("A valid reason for this failure is a binary built without");
936       logmsg("proper network library linkage. This might not be the only");
937       logmsg("reason, but double check it before anything else.");
938       sclose(sock);
939       return CURL_SOCKET_BAD;
940     }
941   }
942 
943   /* start accepting connections */
944   rc = listen(sock, 5);
945   if(0 != rc) {
946     error = SOCKERRNO;
947     logmsg("listen(%" CURL_FORMAT_SOCKET_T ", 5) failed with error: (%d) %s",
948            sock, error, sstrerror(error));
949     sclose(sock);
950     return CURL_SOCKET_BAD;
951   }
952 
953   return sock;
954 }
955 
956 
main(int argc,char * argv[])957 int main(int argc, char *argv[])
958 {
959   curl_socket_t sock = CURL_SOCKET_BAD;
960   curl_socket_t msgsock = CURL_SOCKET_BAD;
961   int wrotepidfile = 0;
962   int wroteportfile = 0;
963   const char *pidname = ".socksd.pid";
964   const char *portname = NULL; /* none by default */
965   bool juggle_again;
966   int error;
967   int arg = 1;
968 
969 #ifdef USE_UNIX_SOCKETS
970   const char *unix_socket = NULL;
971   bool unlink_socket = false;
972 #endif
973 
974   while(argc>arg) {
975     if(!strcmp("--version", argv[arg])) {
976       printf("socksd IPv4%s\n",
977 #ifdef USE_IPV6
978              "/IPv6"
979 #else
980              ""
981 #endif
982              );
983       return 0;
984     }
985     else if(!strcmp("--pidfile", argv[arg])) {
986       arg++;
987       if(argc>arg)
988         pidname = argv[arg++];
989     }
990     else if(!strcmp("--portfile", argv[arg])) {
991       arg++;
992       if(argc>arg)
993         portname = argv[arg++];
994     }
995     else if(!strcmp("--config", argv[arg])) {
996       arg++;
997       if(argc>arg)
998         configfile = argv[arg++];
999     }
1000     else if(!strcmp("--backend", argv[arg])) {
1001       arg++;
1002       if(argc>arg)
1003         backendaddr = argv[arg++];
1004     }
1005     else if(!strcmp("--backendport", argv[arg])) {
1006       arg++;
1007       if(argc>arg)
1008         backendport = (unsigned short)atoi(argv[arg++]);
1009     }
1010     else if(!strcmp("--logfile", argv[arg])) {
1011       arg++;
1012       if(argc>arg)
1013         serverlogfile = argv[arg++];
1014     }
1015     else if(!strcmp("--reqfile", argv[arg])) {
1016       arg++;
1017       if(argc>arg)
1018         reqlogfile = argv[arg++];
1019     }
1020     else if(!strcmp("--ipv6", argv[arg])) {
1021 #ifdef USE_IPV6
1022       socket_domain = AF_INET6;
1023       socket_type = "IPv6";
1024 #endif
1025       arg++;
1026     }
1027     else if(!strcmp("--ipv4", argv[arg])) {
1028       /* for completeness, we support this option as well */
1029 #ifdef USE_IPV6
1030       socket_type = "IPv4";
1031 #endif
1032       arg++;
1033     }
1034     else if(!strcmp("--unix-socket", argv[arg])) {
1035       arg++;
1036       if(argc>arg) {
1037 #ifdef USE_UNIX_SOCKETS
1038         struct sockaddr_un sau;
1039         unix_socket = argv[arg];
1040         if(strlen(unix_socket) >= sizeof(sau.sun_path)) {
1041           fprintf(stderr,
1042                   "socksd: socket path must be shorter than %zu chars: %s\n",
1043               sizeof(sau.sun_path), unix_socket);
1044           return 0;
1045         }
1046         socket_domain = AF_UNIX;
1047         socket_type = "unix";
1048 #endif
1049         arg++;
1050       }
1051     }
1052     else if(!strcmp("--port", argv[arg])) {
1053       arg++;
1054       if(argc>arg) {
1055         char *endptr;
1056         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1057         port = curlx_ultous(ulnum);
1058         arg++;
1059       }
1060     }
1061     else {
1062       puts("Usage: socksd [option]\n"
1063            " --backend [ipv4 addr]\n"
1064            " --backendport [TCP port]\n"
1065            " --config [file]\n"
1066            " --version\n"
1067            " --logfile [file]\n"
1068            " --pidfile [file]\n"
1069            " --portfile [file]\n"
1070            " --reqfile [file]\n"
1071            " --ipv4\n"
1072            " --ipv6\n"
1073            " --unix-socket [file]\n"
1074            " --bindonly\n"
1075            " --port [port]\n");
1076       return 0;
1077     }
1078   }
1079 
1080 #ifdef _WIN32
1081   win32_init();
1082   atexit(win32_cleanup);
1083 
1084   setmode(fileno(stdin), O_BINARY);
1085   setmode(fileno(stdout), O_BINARY);
1086   setmode(fileno(stderr), O_BINARY);
1087 #endif
1088 
1089   install_signal_handlers(false);
1090 
1091   sock = socket(socket_domain, SOCK_STREAM, 0);
1092 
1093   if(CURL_SOCKET_BAD == sock) {
1094     error = SOCKERRNO;
1095     logmsg("Error creating socket: (%d) %s",
1096            error, sstrerror(error));
1097     goto socks5_cleanup;
1098   }
1099 
1100   {
1101     /* passive daemon style */
1102     sock = sockdaemon(sock, &port
1103 #ifdef USE_UNIX_SOCKETS
1104             , unix_socket
1105 #endif
1106             );
1107     if(CURL_SOCKET_BAD == sock) {
1108       goto socks5_cleanup;
1109     }
1110 #ifdef USE_UNIX_SOCKETS
1111     unlink_socket = true;
1112 #endif
1113     msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
1114   }
1115 
1116   logmsg("Running %s version", socket_type);
1117 
1118 #ifdef USE_UNIX_SOCKETS
1119   if(socket_domain == AF_UNIX)
1120     logmsg("Listening on unix socket %s", unix_socket);
1121   else
1122 #endif
1123   logmsg("Listening on port %hu", port);
1124 
1125   wrotepidfile = write_pidfile(pidname);
1126   if(!wrotepidfile) {
1127     goto socks5_cleanup;
1128   }
1129 
1130   if(portname) {
1131     wroteportfile = write_portfile(portname, port);
1132     if(!wroteportfile) {
1133       goto socks5_cleanup;
1134     }
1135   }
1136 
1137   do {
1138     juggle_again = incoming(sock);
1139   } while(juggle_again);
1140 
1141 socks5_cleanup:
1142 
1143   if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
1144     sclose(msgsock);
1145 
1146   if(sock != CURL_SOCKET_BAD)
1147     sclose(sock);
1148 
1149 #ifdef USE_UNIX_SOCKETS
1150   if(unlink_socket && socket_domain == AF_UNIX) {
1151     error = unlink(unix_socket);
1152     logmsg("unlink(%s) = %d (%s)", unix_socket, error, strerror(error));
1153   }
1154 #endif
1155 
1156   if(wrotepidfile)
1157     unlink(pidname);
1158   if(wroteportfile)
1159     unlink(portname);
1160 
1161   restore_signal_handlers(false);
1162 
1163   if(got_exit_signal) {
1164     logmsg("============> socksd exits with signal (%d)", exit_signal);
1165     /*
1166      * To properly set the return status of the process we
1167      * must raise the same signal SIGINT or SIGTERM that we
1168      * caught and let the old handler take care of it.
1169      */
1170     raise(exit_signal);
1171   }
1172 
1173   logmsg("============> socksd quits");
1174   return 0;
1175 }
1176