Fixed hang on race condition with UDP-RECV and fork
[socat.git] / xio-socks.c
blobaa1070edd2956d91cc864f57117f507a7e8057ff
1 /* source: xio-socks.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for opening addresses of socks4 type */
7 #include "xiosysincludes.h"
9 #if WITH_SOCKS4 || WITH_SOCKS4A
11 #include "xioopen.h"
12 #include "xio-ascii.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h"
15 #include "xio-ipapp.h"
17 #include "xio-socks.h"
20 enum {
21 SOCKS_CD_GRANTED = 90,
22 SOCKS_CD_FAILED,
23 SOCKS_CD_NOIDENT,
24 SOCKS_CD_IDENTFAILED
25 } ;
27 #define SOCKSPORT "1080"
28 #define BUFF_LEN (SIZEOF_STRUCT_SOCKS4+512)
30 static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts,
31 int xioflags, xiofile_t *fd,
32 unsigned groups, int dummy1, int dummy2,
33 int dummy3);
35 const struct optdesc opt_socksport = { "socksport", NULL, OPT_SOCKSPORT, GROUP_IP_SOCKS4, PH_LATE, TYPE_STRING, OFUNC_SPEC };
36 const struct optdesc opt_socksuser = { "socksuser", NULL, OPT_SOCKSUSER, GROUP_IP_SOCKS4, PH_LATE, TYPE_NAME, OFUNC_SPEC };
38 const struct addrdesc addr_socks4_connect = { "socks4", 3, xioopen_socks4_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<socks-server>:<host>:<port>") };
40 const struct addrdesc addr_socks4a_connect = { "socks4a", 3, xioopen_socks4_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, 1, 0, 0 HELP(":<socks-server>:<host>:<port>") };
42 static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts,
43 int xioflags, xiofile_t *xxfd,
44 unsigned groups, int socks4a, int dummy2,
45 int dummy3) {
46 /* we expect the form: host:host:port */
47 struct single *xfd = &xxfd->stream;
48 struct opt *opts0 = NULL;
49 const char *sockdname; char *socksport;
50 const char *targetname, *targetport;
51 int pf = PF_UNSPEC;
52 int ipproto = IPPROTO_TCP;
53 bool dofork = false;
54 union sockaddr_union us_sa, *us = &us_sa;
55 union sockaddr_union them_sa, *them = &them_sa;
56 socklen_t uslen = sizeof(us_sa);
57 socklen_t themlen = sizeof(them_sa);
58 bool needbind = false;
59 bool lowport = false;
60 unsigned char buff[BUFF_LEN];
61 struct socks4 *sockhead = (struct socks4 *)buff;
62 size_t buflen = sizeof(buff);
63 int socktype = SOCK_STREAM;
64 int level;
65 int result;
67 if (argc != 4) {
68 Error1("%s: 3 parameters required", argv[0]);
69 return STAT_NORETRY;
71 sockdname = argv[1];
72 targetname = argv[2];
73 targetport = argv[3];
75 xfd->howtoend = END_SHUTDOWN;
76 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
77 applyopts(-1, opts, PH_INIT);
79 retropt_int(opts, OPT_SO_TYPE, &socktype);
81 retropt_bool(opts, OPT_FORK, &dofork);
83 result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen);
84 if (result != STAT_OK) return result;
85 result =
86 _xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
87 &pf, ipproto,
88 xfd->para.socket.ip.res_opts[1],
89 xfd->para.socket.ip.res_opts[0],
90 them, &themlen, us, &uslen,
91 &needbind, &lowport, socktype);
93 Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
94 targetname,
95 ntohs(sockhead->port),
96 sockdname, socksport, sockhead->userid);
98 do { /* loop over failed connect and socks-request attempts */
100 #if WITH_RETRY
101 if (xfd->forever || xfd->retry) {
102 level = E_INFO;
103 } else
104 #endif /* WITH_RETRY */
105 level = E_ERROR;
107 /* we try to resolve the target address _before_ connecting to the socks
108 server: this avoids unnecessary socks connects and timeouts */
109 result =
110 _xioopen_socks4_connect0(xfd, targetname, socks4a, sockhead,
111 (ssize_t *)&buflen, level);
112 switch (result) {
113 case STAT_OK: break;
114 #if WITH_RETRY
115 case STAT_RETRYLATER:
116 case STAT_RETRYNOW:
117 if (xfd->forever || xfd->retry--) {
118 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
119 continue;
121 #endif /* WITH_RETRY */
122 default:
123 return result;
126 /* this cannot fork because we retrieved fork option above */
127 result =
128 _xioopen_connect (xfd,
129 needbind?(struct sockaddr *)us:NULL, sizeof(*us),
130 (struct sockaddr *)them, themlen,
131 opts, pf, socktype, IPPROTO_TCP, lowport, level);
132 switch (result) {
133 case STAT_OK: break;
134 #if WITH_RETRY
135 case STAT_RETRYLATER:
136 case STAT_RETRYNOW:
137 if (xfd->forever || xfd->retry--) {
138 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
139 continue;
141 #endif /* WITH_RETRY */
142 default:
143 return result;
146 applyopts(xfd->fd, opts, PH_ALL);
148 if ((result = _xio_openlate(xfd, opts)) < 0)
149 return result;
151 result = _xioopen_socks4_connect(xfd, sockhead, buflen, level);
152 switch (result) {
153 case STAT_OK: break;
154 #if WITH_RETRY
155 case STAT_RETRYLATER:
156 case STAT_RETRYNOW:
157 if (xfd->forever || xfd->retry--) {
158 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
159 continue;
161 #endif /* WITH_RETRY */
162 default:
163 return result;
166 if (dofork) {
167 xiosetchilddied(); /* set SIGCHLD handler */
170 #if WITH_RETRY
171 if (dofork) {
172 pid_t pid;
173 int level = E_ERROR;
174 if (xfd->forever || xfd->retry) {
175 level = E_WARN; /* most users won't expect a problem here,
176 so Notice is too weak */
178 while ((pid = xio_fork(false, level)) < 0) {
179 if (xfd->forever || --xfd->retry) {
180 Nanosleep(&xfd->intervall, NULL);
181 continue;
183 return STAT_RETRYLATER;
186 if (pid == 0) { /* child process */
187 xfd->forever = false; xfd->retry = 0;
188 break;
191 /* parent process */
192 Close(xfd->fd);
193 Nanosleep(&xfd->intervall, NULL);
194 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
195 continue;
196 } else
197 #endif /* WITH_RETRY */
199 break;
202 } while (true); /* end of complete open loop - drop out on success */
203 return 0;
207 int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen) {
208 struct servent *se;
209 char *userid;
211 /* generate socks header - points to final target */
212 sockhead->version = 4;
213 sockhead->action = 1;
214 sockhead->port = parseport(targetport, IPPROTO_TCP); /* network byte
215 order */
217 if (retropt_string(opts, OPT_SOCKSPORT, socksport) < 0) {
218 if ((se = getservbyname("socks", "tcp")) != NULL) {
219 Debug1("\"socks/tcp\" resolves to %u", ntohs(se->s_port));
220 if ((*socksport = Malloc(6)) == NULL) {
221 return -1;
223 sprintf(*socksport, "%u", ntohs(se->s_port));
224 } else {
225 Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT);
226 if ((*socksport = strdup(SOCKSPORT)) == NULL) {
227 errno = ENOMEM; return -1;
232 if (retropt_string(opts, OPT_SOCKSUSER, &userid) < 0) {
233 if ((userid = getenv("LOGNAME")) == NULL) {
234 if ((userid = getenv("USER")) == NULL) {
235 userid = "anonymous";
239 sockhead->userid[0] = '\0'; strncat(sockhead->userid, userid, *headlen-SIZEOF_STRUCT_SOCKS4-1);
240 *headlen = SIZEOF_STRUCT_SOCKS4+strlen(userid)+1;
241 return STAT_OK;
245 /* called within retry/fork loop, before connect() */
247 _xioopen_socks4_connect0(struct single *xfd,
248 const char *hostname, /* socks target host */
249 int socks4a,
250 struct socks4 *sockhead,
251 ssize_t *headlen, /* get available space,
252 return used length*/
253 int level) {
254 int result;
256 if (!socks4a) {
257 union sockaddr_union sau;
258 socklen_t saulen = sizeof(sau);
260 if ((result = xiogetaddrinfo(hostname, NULL,
261 PF_INET, SOCK_STREAM, IPPROTO_TCP,
262 &sau, &saulen,
263 xfd->para.socket.ip.res_opts[1],
264 xfd->para.socket.ip.res_opts[0]))
265 != STAT_OK) {
266 return result; /*! STAT_RETRY? */
268 memcpy(&sockhead->dest, &sau.ip4.sin_addr, 4);
270 #if WITH_SOCKS4A
271 else {
272 /*! noresolve */
273 sockhead->dest = htonl(0x00000001); /* three bytes zero */
275 #endif /* WITH_SOCKS4A */
276 #if WITH_SOCKS4A
277 if (socks4a) {
278 /* SOCKS4A requires us to append the host name to resolve
279 after the user name's trailing 0 byte. */
280 char* insert_position = (char*) sockhead + *headlen;
282 insert_position[0] = '\0'; strncat(insert_position, hostname, BUFF_LEN-*headlen-1);
283 ((char *)sockhead)[BUFF_LEN-1] = 0;
284 *headlen += strlen(hostname) + 1;
285 if (*headlen > BUFF_LEN) {
286 *headlen = BUFF_LEN;
289 #endif /* WITH_SOCKS4A */
290 return STAT_OK;
294 /* perform socks4 client dialog on existing FD.
295 Called within fork/retry loop, after connect() */
296 int _xioopen_socks4_connect(struct single *xfd,
297 struct socks4 *sockhead,
298 size_t headlen,
299 int level) {
300 ssize_t bytes;
301 int result;
302 unsigned char buff[SIZEOF_STRUCT_SOCKS4];
303 struct socks4 *replyhead = (struct socks4 *)buff;
304 char *destdomname = NULL;
306 /* send socks header (target addr+port, +auth) */
307 #if WITH_MSGLEVEL <= E_INFO
308 if (ntohl(sockhead->dest) <= 0x000000ff) {
309 destdomname = strchr(sockhead->userid, '\0')+1;
311 Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s",
312 destdomname?"a":"",
313 sockhead->version, sockhead->action, ntohs(sockhead->port),
314 ((unsigned char *)&sockhead->dest)[0],
315 ((unsigned char *)&sockhead->dest)[1],
316 ((unsigned char *)&sockhead->dest)[2],
317 ((unsigned char *)&sockhead->dest)[3],
318 sockhead->userid,
319 destdomname?" DESTNAME=":"",
320 destdomname?destdomname:"");
321 #endif /* WITH_MSGLEVEL <= E_INFO */
322 #if WITH_MSGLEVEL <= E_DEBUG
324 char *msgbuff;
325 if ((msgbuff = Malloc(3*headlen)) != NULL) {
326 xiohexdump((const unsigned char *)sockhead, headlen, msgbuff);
327 Debug1("sending socks4(a) request data %s", msgbuff);
330 #endif /* WITH_MSGLEVEL <= E_DEBUG */
331 if (writefull(xfd->fd, sockhead, headlen) < 0) {
332 Msg4(level, "write(%d, %p, "F_Zu"): %s",
333 xfd->fd, sockhead, headlen, strerror(errno));
334 if (Close(xfd->fd) < 0) {
335 Info2("close(%d): %s", xfd->fd, strerror(errno));
337 return STAT_RETRYLATER; /* retry complete open cycle */
340 bytes = 0;
341 Info("waiting for socks reply");
342 while (bytes >= 0) { /* loop over answer chunks until complete or error */
343 /* receive socks answer */
344 do {
345 result = Read(xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes);
346 } while (result < 0 && errno == EINTR);
347 if (result < 0) {
348 Msg4(level, "read(%d, %p, "F_Zu"): %s",
349 xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes,
350 strerror(errno));
351 if (Close(xfd->fd) < 0) {
352 Info2("close(%d): %s", xfd->fd, strerror(errno));
355 if (result == 0) {
356 Msg(level, "read(): EOF during read of socks reply, peer might not be a socks4 server");
357 if (Close(xfd->fd) < 0) {
358 Info2("close(%d): %s", xfd->fd, strerror(errno));
360 return STAT_RETRYLATER;
362 #if WITH_MSGLEVEL <= E_DEBUG
364 char msgbuff[3*SIZEOF_STRUCT_SOCKS4];
365 * xiohexdump((const unsigned char *)replyhead+bytes, result, msgbuff)
366 = '\0';
367 Debug2("received socks4 reply data (offset "F_Zd"): %s", bytes, msgbuff);
369 #endif /* WITH_MSGLEVEL <= E_DEBUG */
370 bytes += result;
371 if (bytes == SIZEOF_STRUCT_SOCKS4) {
372 Debug1("received all "F_Zd" bytes", bytes);
373 break;
375 Debug2("received %d bytes, waiting for "F_Zu" more bytes",
376 result, SIZEOF_STRUCT_SOCKS4-bytes);
378 if (result <= 0) { /* we had a problem while reading socks answer */
379 return STAT_RETRYLATER; /* retry complete open cycle */
382 Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u",
383 replyhead->version, replyhead->action, ntohs(replyhead->port),
384 ((uint8_t *)&replyhead->dest)[0],
385 ((uint8_t *)&replyhead->dest)[1],
386 ((uint8_t *)&replyhead->dest)[2],
387 ((uint8_t *)&replyhead->dest)[3]);
388 if (replyhead->version != 0) {
389 Warn1("socks: reply code version is not 0 (%d)",
390 replyhead->version);
393 switch (replyhead->action) {
394 case SOCKS_CD_GRANTED:
395 /* Notice("socks: connect request succeeded"); */
396 #if 0
397 if (Getsockname(xfd->fd, (struct sockaddr *)&us, &uslen) < 0) {
398 Warn4("getsockname(%d, %p, {%d}): %s",
399 xfd->fd, &us, uslen, strerror(errno));
401 Notice1("successfully connected from %s via socks4",
402 sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff)));
403 #else
404 Notice("successfully connected via socks4");
405 #endif
406 break;
408 case SOCKS_CD_FAILED:
409 Msg(level, "socks: connect request rejected or failed");
410 return STAT_RETRYLATER;
412 case SOCKS_CD_NOIDENT:
413 Msg(level, "socks: ident refused by client");
414 return STAT_RETRYLATER;
416 case SOCKS_CD_IDENTFAILED:
417 Msg(level, "socks: ident failed");
418 return STAT_RETRYLATER;
420 default:
421 Msg1(level, "socks: undefined status %u", replyhead->action);
424 return STAT_OK;
426 #endif /* WITH_SOCKS4 || WITH_SOCKS4A */