nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /* source: xio-socks.c */
2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4  
5 /* this file contains the source for opening addresses of socks4 type */
6  
7 #include "xiosysincludes.h"
8  
9 #if WITH_SOCKS4 || WITH_SOCKS4A
10  
11 #include "xioopen.h"
12 #include "xio-ascii.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h"
15 #include "xio-ipapp.h"
16  
17 #include "xio-socks.h"
18  
19  
20 enum {
21 SOCKS_CD_GRANTED = 90,
22 SOCKS_CD_FAILED,
23 SOCKS_CD_NOIDENT,
24 SOCKS_CD_IDENTFAILED
25 } ;
26  
27 #define SOCKSPORT "1080"
28 #define BUFF_LEN (SIZEOF_STRUCT_SOCKS4+512)
29  
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);
34  
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 };
37  
38 static const struct xioaddr_inter_desc xiointer_socks4_connect2 = { XIOADDR_INTER, "socks4", 2, XIOBIT_ALL, GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_socks4_connect, 0, 0, 0, XIOBIT_RDWR HELP(":<host>:<port>") };
39 static const struct xioaddr_endpoint_desc xioendpoint_socks4_connect3 = { XIOADDR_ENDPOINT, "socks4", 3, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_socks4_connect, 0, 0, 0 HELP(":<socks-server>:<host>:<port>") };
40  
41 const union xioaddr_desc *xioaddrs_socks4_connect[] = {
42 (union xioaddr_desc *)&xiointer_socks4_connect2,
43 (union xioaddr_desc *)&xioendpoint_socks4_connect3,
44 NULL
45 };
46  
47 static const struct xioaddr_inter_desc xiointer_socks4a_connect2 = { XIOADDR_INTER, "socks4a", 2, XIOBIT_ALL, GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_socks4_connect, 1, 0, 0, XIOBIT_RDWR HELP(":<host>:<port>") };
48 static const struct xioaddr_endpoint_desc xioendpoint_socks4a_connect3 = { XIOADDR_ENDPOINT, "socks4a", 3, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_socks4_connect, 1, 0, 0 HELP(":<socks-server>:<host>:<port>") };
49  
50 const union xioaddr_desc *xioaddrs_socks4a_connect[] = {
51 (union xioaddr_desc *)&xiointer_socks4a_connect2,
52 (union xioaddr_desc *)&xioendpoint_socks4a_connect3,
53 NULL
54 };
55  
56 /*!!! should be two different functions */
57 static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts,
58 int xioflags, xiofile_t *xxfd,
59 unsigned groups, int socks4a, int dummy2,
60 int dummy3) {
61 /* we expect the form: host:host:port */
62 struct single *xfd = &xxfd->stream;
63 int rw = (xioflags&XIO_ACCMODE);
64 struct opt *opts0 = NULL;
65 const char *sockdname; char *sockdport;
66 const char *targetname, *targetport;
67 int pf = PF_UNSPEC;
68 int ipproto = IPPROTO_TCP;
69 bool dofork = false;
70 union sockaddr_union us_sa, *us = &us_sa;
71 union sockaddr_union them_sa, *them = &them_sa;
72 socklen_t uslen = sizeof(us_sa);
73 socklen_t themlen = sizeof(them_sa);
74 bool needbind = false;
75 bool lowport = false;
76 unsigned char buff[BUFF_LEN];
77 struct socks4request *sockhead = (struct socks4request *)buff;
78 size_t buflen = sizeof(buff);
79 int socktype = SOCK_STREAM;
80 int level;
81 int result;
82  
83 if (argc < 3 || argc > 4) {
84 Warn("syntax 1 (terminal): socks-connect:<socks-server>:<host>:<port>");
85 Error("syntax 2 (inter): socks-connect:<host>:<port>");
86 return STAT_NORETRY;
87 }
88  
89 if (argc == 3) {
90 if (xfd->rfd < 0) {
91 Error("xioopen_socks4_connect(): socksservername missing");
92 return STAT_NORETRY;
93 }
94 sockdname = NULL;
95 targetname = argv[1];
96 targetport = argv[2];
97 } else /* if (argc == 4) */ {
98 if (xfd->rfd >= 0) {
99 Error("xioopen_socks4_connect(): socksservername not allowed here");
100 return STAT_NORETRY;
101 }
102 sockdname = argv[1];
103 targetname = argv[2];
104 targetport = argv[3];
105 }
106  
107 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
108 applyopts(-1, opts, PH_INIT);
109  
110 retropt_int(opts, OPT_SO_TYPE, &socktype);
111  
112 retropt_bool(opts, OPT_FORK, &dofork);
113  
114 result = _xioopen_socks4_prepare(targetport, opts, &sockdport, sockhead, &buflen);
115 if (result != STAT_OK) return result;
116 if (xfd->wfd < 0) {
117 result =
118 _xioopen_ipapp_prepare(opts, &opts0, sockdname, sockdport,
119 &pf, ipproto,
120 xfd->para.socket.ip.res_opts[1],
121 xfd->para.socket.ip.res_opts[0],
122 them, &themlen, us, &uslen,
123 &needbind, &lowport, socktype);
124 if (result != STAT_OK) return result;
125  
126 Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
127 targetname,
128 ntohs(sockhead->port),
129 sockdname, sockdport, sockhead->userid);
130 } else {
131 Notice3("opening connection to %s:%u using socks4 connect as user \"%s\"",
132 targetname, ntohs(sockhead->port), sockhead->userid);
133 }
134  
135 do { /* loop over failed connect and socks-request attempts */
136  
137 #if WITH_RETRY
138 if (xfd->forever || xfd->retry) {
139 level = E_INFO;
140 } else
141 #endif /* WITH_RETRY */
142 level = E_ERROR;
143  
144 /* we try to resolve the target address _before_ connecting to the socks
145 server: this avoids unnecessary socks connects and timeouts */
146 result =
147 _xioopen_socks4_connect0(xfd, targetname, socks4a, sockhead,
148 (ssize_t *)&buflen, level);
149 switch (result) {
150 case STAT_OK: break;
151 #if WITH_RETRY
152 case STAT_RETRYLATER:
153 case STAT_RETRYNOW:
154 if (xfd->forever || xfd->retry--) {
155 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
156 continue;
157 }
158 #endif /* WITH_RETRY */
159 default:
160 return result;
161 }
162  
163 if (xfd->wfd < 0) {
164 /* this cannot fork because we retrieved fork option above */
165 result =
166 _xioopen_connect (xfd,
167 needbind?(struct sockaddr *)us:NULL, sizeof(*us),
168 (struct sockaddr *)them, themlen,
169 opts, pf, socktype, IPPROTO_TCP, lowport, level);
170 switch (result) {
171 case STAT_OK: break;
172 #if WITH_RETRY
173 case STAT_RETRYLATER:
174 case STAT_RETRYNOW:
175 if (xfd->forever || xfd->retry--) {
176 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
177 continue;
178 }
179 #endif /* WITH_RETRY */
180 default:
181 return result;
182 }
183 if (XIOWITHWR(rw)) xfd->wfd = xfd->rfd;
184 if (!XIOWITHRD(rw)) xfd->rfd = -1;
185 } else {
186 xfd->dtype = XIODATA_STREAM;
187 }
188  
189 /*!*/
190 applyopts(xfd->rfd, opts, PH_ALL);
191  
192 if ((result = _xio_openlate(xfd, opts)) < 0)
193 return result;
194  
195 result = _xioopen_socks4_connect(xfd, sockhead, buflen, level);
196 switch (result) {
197 case STAT_OK: break;
198 #if WITH_RETRY
199 case STAT_RETRYLATER:
200 case STAT_RETRYNOW:
201 if (xfd->forever || xfd->retry--) {
202 if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL);
203 continue;
204 }
205 #endif /* WITH_RETRY */
206 default:
207 return result;
208 }
209  
210 if (dofork) {
211 xiosetchilddied(); /* set SIGCHLD handler */
212 }
213  
214 #if WITH_RETRY
215 if (dofork) {
216 pid_t pid;
217 int level = E_ERROR;
218 if (xfd->forever || xfd->retry) {
219 level = E_WARN; /* most users won't expect a problem here,
220 so Notice is too weak */
221 }
222 while ((pid = xio_fork(false, level)) < 0) {
223 if (xfd->forever || --xfd->retry) {
224 Nanosleep(&xfd->intervall, NULL);
225 continue;
226 }
227 return STAT_RETRYLATER;
228 }
229  
230 if (pid == 0) { /* child process */
231 xfd->forever = false; xfd->retry = 0;
232 break;
233 }
234  
235 /* parent process */
236 Notice1("forked off child process "F_pid, pid);
237 Close(xfd->rfd);
238 Close(xfd->wfd);
239 Nanosleep(&xfd->intervall, NULL);
240 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
241 continue;
242 } else
243 #endif /* WITH_RETRY */
244 {
245 break;
246 }
247  
248 } while (true); /* end of complete open loop - drop out on success */
249 return 0;
250 }
251  
252  
253 int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4request *sockhead, size_t *headlen) {
254 struct servent *se;
255 char *userid;
256  
257 /* generate socks header - points to final target */
258 sockhead->version = 4;
259 sockhead->action = 1;
260 sockhead->port = parseport(targetport, IPPROTO_TCP); /* network byte
261 order */
262  
263 if (retropt_string(opts, OPT_SOCKSPORT, socksport) < 0) {
264 if ((se = getservbyname("socks", "tcp")) != NULL) {
265 Debug1("\"socks/tcp\" resolves to %u", ntohs(se->s_port));
266 if ((*socksport = Malloc(6)) == NULL) {
267 return -1;
268 }
269 sprintf(*socksport, "%u", ntohs(se->s_port));
270 } else {
271 Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT);
272 if ((*socksport = strdup(SOCKSPORT)) == NULL) {
273 errno = ENOMEM; return -1;
274 }
275 }
276 }
277  
278 if (retropt_string(opts, OPT_SOCKSUSER, &userid) < 0) {
279 if ((userid = getenv("LOGNAME")) == NULL) {
280 if ((userid = getenv("USER")) == NULL) {
281 userid = "anonymous";
282 }
283 }
284 }
285 sockhead->userid[0] = '\0'; strncat(sockhead->userid, userid, *headlen-SIZEOF_STRUCT_SOCKS4-1);
286 *headlen = SIZEOF_STRUCT_SOCKS4+strlen(userid)+1;
287 return STAT_OK;
288 }
289  
290  
291 /* called within retry/fork loop, before connect() */
292 int
293 _xioopen_socks4_connect0(struct single *xfd,
294 const char *hostname, /* socks target host */
295 int socks4a,
296 struct socks4request *sockhead,
297 ssize_t *headlen, /* get available space,
298 return used length*/
299 int level) {
300 int result;
301  
302 if (!socks4a) {
303 union sockaddr_union sau;
304 socklen_t saulen = sizeof(sau);
305  
306 if ((result = xiogetaddrinfo(hostname, NULL,
307 PF_INET, SOCK_STREAM, IPPROTO_TCP,
308 &sau, &saulen,
309 xfd->para.socket.ip.res_opts[1],
310 xfd->para.socket.ip.res_opts[0]))
311 != STAT_OK) {
312 return result; /*! STAT_RETRY? */
313 }
314 memcpy(&sockhead->dest, &sau.ip4.sin_addr, 4);
315 }
316 #if WITH_SOCKS4A
317 else {
318 /*! noresolve */
319 sockhead->dest = htonl(0x00000001); /* three bytes zero */
320 }
321 #endif /* WITH_SOCKS4A */
322 #if WITH_SOCKS4A
323 if (socks4a) {
324 /* SOCKS4A requires us to append the host name to resolve
325 after the user name's trailing 0 byte. */
326 char* insert_position = (char*) sockhead + *headlen;
327  
328 insert_position[0] = '\0'; strncat(insert_position, hostname, BUFF_LEN-*headlen-1);
329 ((char *)sockhead)[BUFF_LEN-1] = 0;
330 *headlen += strlen(hostname) + 1;
331 if (*headlen > BUFF_LEN) {
332 *headlen = BUFF_LEN;
333 }
334 }
335 #endif /* WITH_SOCKS4A */
336 return STAT_OK;
337 }
338  
339  
340 /* perform socks4 client dialog on existing FD.
341 Called within fork/retry loop, after connect() */
342 int _xioopen_socks4_connect(struct single *xfd,
343 struct socks4request *sockhead,
344 size_t headlen,
345 int level) {
346 ssize_t bytes;
347 int wfd;
348 int result;
349 unsigned char buff[SIZEOF_STRUCT_SOCKS4];
350 struct socks4head *replyhead = (struct socks4head *)buff;
351 char *destdomname = NULL;
352  
353 wfd =xfd->wfd;
354  
355 /* send socks header (target addr+port, +auth) */
356 #if WITH_MSGLEVEL <= E_INFO
357 if (ntohl(sockhead->dest) <= 0x000000ff) {
358 destdomname = strchr(sockhead->userid, '\0')+1;
359 }
360 Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s",
361 destdomname?"a":"",
362 sockhead->version, sockhead->action, ntohs(sockhead->port),
363 ((unsigned char *)&sockhead->dest)[0],
364 ((unsigned char *)&sockhead->dest)[1],
365 ((unsigned char *)&sockhead->dest)[2],
366 ((unsigned char *)&sockhead->dest)[3],
367 sockhead->userid,
368 destdomname?" DESTNAME=":"",
369 destdomname?destdomname:"");
370 #endif /* WITH_MSGLEVEL <= E_INFO */
371 #if WITH_MSGLEVEL <= E_DEBUG
372 {
373 char *msgbuff;
374 if ((msgbuff = Malloc(3*headlen)) != NULL) {
375 xiohexdump((const unsigned char *)sockhead, headlen, msgbuff);
376 Debug1("sending socks4(a) request data %s", msgbuff);
377 }
378 }
379 #endif /* WITH_MSGLEVEL <= E_DEBUG */
380 if (writefull(wfd, sockhead, headlen) < 0) {
381 Msg4(level, "write(%d, %p, "F_Zu"): %s",
382 wfd, sockhead, headlen, strerror(errno));
383 if (Close(wfd) < 0) {
384 Info2("close(%d): %s", wfd, strerror(errno));
385 }
386 if (Close(xfd->rfd) < 0) {
387 Info2("close(%d): %s", xfd->rfd, strerror(errno));
388 }
389 return STAT_RETRYLATER; /* retry complete open cycle */
390 }
391  
392 bytes = 0;
393 Info("waiting for socks reply");
394 while (bytes >= 0) { /* loop over answer chunks until complete or error */
395 /* receive socks answer */
396 do {
397 result = Read(xfd->rfd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes);
398 } while (result < 0 && errno == EINTR);
399 if (result < 0) {
400 Msg4(level, "read(%d, %p, "F_Zu"): %s",
401 xfd->rfd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes,
402 strerror(errno));
403 if (Close(xfd->rfd) < 0) {
404 Info2("close(%d): %s", xfd->rfd, strerror(errno));
405 }
406 if (Close(wfd) < 0) {
407 Info2("close(%d): %s", wfd, strerror(errno));
408 }
409 }
410 if (result == 0) {
411 Msg(level, "read(): EOF during read of socks reply, peer might not be a socks4 server");
412 if (Close(xfd->rfd) < 0) {
413 Info2("close(%d): %s", xfd->rfd, strerror(errno));
414 }
415 if (Close(wfd) < 0) {
416 Info2("close(%d): %s", wfd, strerror(errno));
417 }
418 return STAT_RETRYLATER;
419 }
420 #if WITH_MSGLEVEL <= E_DEBUG
421 {
422 char msgbuff[3*SIZEOF_STRUCT_SOCKS4];
423 * xiohexdump((const unsigned char *)replyhead+bytes, result, msgbuff)
424 = '\0';
425 Debug2("received socks4 reply data (offset "F_Zd"): %s", bytes, msgbuff);
426 }
427 #endif /* WITH_MSGLEVEL <= E_DEBUG */
428 bytes += result;
429 if (bytes == SIZEOF_STRUCT_SOCKS4) {
430 Debug1("received all "F_Zd" bytes", bytes);
431 break;
432 }
433 Debug2("received %d bytes, waiting for "F_Zu" more bytes",
434 result, SIZEOF_STRUCT_SOCKS4-bytes);
435 }
436 if (result <= 0) { /* we had a problem while reading socks answer */
437 return STAT_RETRYLATER; /* retry complete open cycle */
438 }
439  
440 Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u",
441 replyhead->version, replyhead->action, ntohs(replyhead->port),
442 ((uint8_t *)&replyhead->dest)[0],
443 ((uint8_t *)&replyhead->dest)[1],
444 ((uint8_t *)&replyhead->dest)[2],
445 ((uint8_t *)&replyhead->dest)[3]);
446 if (replyhead->version != 0) {
447 Warn1("socks: reply code version is not 0 (%d)",
448 replyhead->version);
449 }
450  
451 switch (replyhead->action) {
452 case SOCKS_CD_GRANTED:
453 /* Notice("socks: connect request succeeded"); */
454 #if 0
455 if (Getsockname(xfd->fd, (struct sockaddr *)&us, &uslen) < 0) {
456 Warn4("getsockname(%d, %p, {%d}): %s",
457 xfd->fd, &us, uslen, strerror(errno));
458 }
459 Notice1("successfully connected from %s via socks4",
460 sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff)));
461 #else
462 Notice("successfully connected via socks4");
463 #endif
464 break;
465  
466 case SOCKS_CD_FAILED:
467 Msg(level, "socks: connect request rejected or failed");
468 return STAT_RETRYLATER;
469  
470 case SOCKS_CD_NOIDENT:
471 Msg(level, "socks: ident refused by client");
472 return STAT_RETRYLATER;
473  
474 case SOCKS_CD_IDENTFAILED:
475 Msg(level, "socks: ident failed");
476 return STAT_RETRYLATER;
477  
478 default:
479 Msg1(level, "socks: undefined status %u", replyhead->action);
480 }
481  
482 return STAT_OK;
483 }
484 #endif /* WITH_SOCKS4 || WITH_SOCKS4A */
485