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
12 #include "xio-ascii.h"
13 #include "xio-socket.h"
15 #include "xio-ipapp.h"
17 #include "xio-socks.h"
21 SOCKS_CD_GRANTED
= 90,
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
, int xioflags
, xiofile_t
*fd
, const struct addrdesc
*addrdesc
);
32 const struct optdesc opt_socksport
= { "socksport", NULL
, OPT_SOCKSPORT
, GROUP_IP_SOCKS4
, PH_LATE
, TYPE_STRING
, OFUNC_SPEC
};
33 const struct optdesc opt_socksuser
= { "socksuser", NULL
, OPT_SOCKSUSER
, GROUP_IP_SOCKS4
, PH_LATE
, TYPE_NAME
, OFUNC_SPEC
};
35 const struct addrdesc xioaddr_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>") };
37 const struct addrdesc xioaddr_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>") };
39 static int xioopen_socks4_connect(
45 const struct addrdesc
*addrdesc
)
47 /* we expect the form: host:host:port */
48 struct single
*sfd
= &xxfd
->stream
;
49 int socks4a
= addrdesc
->arg1
;
50 struct opt
*opts0
= NULL
;
51 const char *sockdname
; char *socksport
;
52 const char *targetname
, *targetport
;
54 int ipproto
= IPPROTO_TCP
;
56 union sockaddr_union us_sa
, *us
= &us_sa
;
57 socklen_t uslen
= sizeof(us_sa
);
58 struct addrinfo
*themlist
, *themp
;
59 struct addrinfo
**ai_sorted
;
61 bool needbind
= false;
64 unsigned char buff
[BUFF_LEN
];
65 struct socks4
*sockhead
= (struct socks4
*)buff
;
66 size_t buflen
= sizeof(buff
);
67 int socktype
= SOCK_STREAM
;
72 xio_syntax(argv
[0], 3, argc
-1, addrdesc
->syntax
);
79 if (sfd
->howtoend
== END_UNSPEC
)
80 sfd
->howtoend
= END_SHUTDOWN
;
81 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0) return -1;
82 applyopts(sfd
, 1, opts
, PH_INIT
);
84 retropt_int(opts
, OPT_SO_TYPE
, &socktype
);
86 retropt_bool(opts
, OPT_FORK
, &dofork
);
88 result
= _xioopen_socks4_prepare(targetport
, opts
, &socksport
, sockhead
, &buflen
);
89 if (result
!= STAT_OK
)
92 Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
94 ntohs(sockhead
->port
),
95 sockdname
, socksport
, sockhead
->userid
);
98 do { /* loop over retries (failed connect and socks-request attempts) */
103 _xioopen_ipapp_prepare(opts
, &opts0
, sockdname
, socksport
,
105 sfd
->para
.socket
.ip
.ai_flags
,
106 &themlist
, us
, &uslen
,
107 &needbind
, &lowport
, socktype
);
109 /* Count addrinfo entries */
112 while (themp
!= NULL
) {
114 themp
= themp
->ai_next
;
116 ai_sorted
= Calloc((i
+1), sizeof(struct addrinfo
*));
117 if (ai_sorted
== NULL
)
118 return STAT_RETRYLATER
;
119 /* Generate a list of addresses sorted by preferred ip version */
120 _xio_sort_ip_addresses(themlist
, ai_sorted
);
122 /* we try to resolve the target address _before_ connecting to the socks
123 server: this avoids unnecessary socks connects and timeouts */
125 _xioopen_socks4_connect0(sfd
, targetname
, socks4a
, sockhead
,
126 (ssize_t
*)&buflen
, level
);
130 case STAT_RETRYLATER
:
132 if (sfd
->forever
|| sfd
->retry
--) {
133 if (result
== STAT_RETRYLATER
)
134 Nanosleep(&sfd
->intervall
, NULL
);
137 #endif /* WITH_RETRY */
142 /* loop over themlist */
144 themp
= ai_sorted
[i
++];
145 while (themp
!= NULL
) {
146 Notice1("opening connection to %s",
147 sockaddr_info(themp
->ai_addr
, themp
->ai_addrlen
,
148 infobuff
, sizeof(infobuff
)));
150 if (sfd
->forever
|| sfd
->retry
|| ai_sorted
[i
] != NULL
) {
153 #endif /* WITH_RETRY */
156 /* this cannot fork because we retrieved fork option above */
158 _xioopen_connect(sfd
,
159 needbind
?us
:NULL
, uslen
,
160 themp
->ai_addr
, themp
->ai_addrlen
,
161 opts
, pf
?pf
:themp
->ai_family
, socktype
, IPPROTO_TCP
, lowport
, level
);
162 if (result
== STAT_OK
)
164 themp
= ai_sorted
[i
++];
166 result
= STAT_RETRYLATER
;
171 case STAT_RETRYLATER
:
173 if (sfd
->forever
|| sfd
->retry
) {
175 if (result
== STAT_RETRYLATER
)
176 Nanosleep(&sfd
->intervall
, NULL
);
179 #endif /* WITH_RETRY */
182 xiofreeaddrinfo(themlist
);
185 xiofreeaddrinfo(themlist
);
186 applyopts(sfd
, -1, opts
, PH_ALL
);
188 if ((result
= _xio_openlate(sfd
, opts
)) < 0)
191 result
= _xioopen_socks4_connect(sfd
, sockhead
, buflen
, level
);
195 case STAT_RETRYLATER
:
197 if (sfd
->forever
|| sfd
->retry
--) {
198 if (result
== STAT_RETRYLATER
) Nanosleep(&sfd
->intervall
, NULL
);
201 #endif /* WITH_RETRY */
207 xiosetchilddied(); /* set SIGCHLD handler */
214 if (sfd
->forever
|| sfd
->retry
) {
215 level
= E_WARN
; /* most users won't expect a problem here,
216 so Notice is too weak */
218 while ((pid
= xio_fork(false, level
, sfd
->shutup
)) < 0) {
219 if (sfd
->forever
|| --sfd
->retry
) {
220 Nanosleep(&sfd
->intervall
, NULL
);
223 return STAT_RETRYLATER
;
226 if (pid
== 0) { /* child process */
227 sfd
->forever
= false;
234 Nanosleep(&sfd
->intervall
, NULL
);
235 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
238 #endif /* WITH_RETRY */
243 } while (true); /* end of complete open loop - drop out on success */
248 int _xioopen_socks4_prepare(const char *targetport
, struct opt
*opts
, char **socksport
, struct socks4
*sockhead
, size_t *headlen
) {
252 /* generate socks header - points to final target */
253 sockhead
->version
= 4;
254 sockhead
->action
= 1;
255 sockhead
->port
= parseport(targetport
, IPPROTO_TCP
); /* network byte
258 if (retropt_string(opts
, OPT_SOCKSPORT
, socksport
) < 0) {
259 if ((se
= getservbyname("socks", "tcp")) != NULL
) {
260 Debug1("\"socks/tcp\" resolves to %u", ntohs(se
->s_port
));
261 if ((*socksport
= Malloc(6)) == NULL
) {
264 sprintf(*socksport
, "%u", ntohs(se
->s_port
));
266 Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT
);
267 if ((*socksport
= strdup(SOCKSPORT
)) == NULL
) {
268 errno
= ENOMEM
; return -1;
273 if (retropt_string(opts
, OPT_SOCKSUSER
, &userid
) < 0) {
274 if ((userid
= getenv("LOGNAME")) == NULL
) {
275 if ((userid
= getenv("USER")) == NULL
) {
276 userid
= "anonymous";
280 sockhead
->userid
[0] = '\0'; strncat(sockhead
->userid
, userid
, *headlen
-SIZEOF_STRUCT_SOCKS4
-1);
281 *headlen
= SIZEOF_STRUCT_SOCKS4
+strlen(userid
)+1;
286 /* called within retry/fork loop, before connect() */
288 _xioopen_socks4_connect0(struct single
*sfd
,
289 const char *hostname
, /* socks target host */
291 struct socks4
*sockhead
,
292 ssize_t
*headlen
, /* get available space,
298 union sockaddr_union sau
;
299 socklen_t saulen
= sizeof(sau
);
301 if ((result
= xioresolve(hostname
, NULL
,
302 PF_INET
, SOCK_STREAM
, IPPROTO_TCP
,
304 sfd
->para
.socket
.ip
.ai_flags
))
306 return result
; /*! STAT_RETRY? */
308 memcpy(&sockhead
->dest
, &sau
.ip4
.sin_addr
, 4);
313 sockhead
->dest
= htonl(0x00000001); /* three bytes zero */
315 #endif /* WITH_SOCKS4A */
318 /* SOCKS4A requires us to append the host name to resolve
319 after the user name's trailing 0 byte. */
320 char* insert_position
= (char*) sockhead
+ *headlen
;
322 insert_position
[0] = '\0'; strncat(insert_position
, hostname
, BUFF_LEN
-*headlen
-1);
323 ((char *)sockhead
)[BUFF_LEN
-1] = 0;
324 *headlen
+= strlen(hostname
) + 1;
325 if (*headlen
> BUFF_LEN
) {
329 #endif /* WITH_SOCKS4A */
334 /* perform socks4 client dialog on existing FD.
335 Called within fork/retry loop, after connect() */
336 int _xioopen_socks4_connect(struct single
*sfd
,
337 struct socks4
*sockhead
,
342 unsigned char buff
[SIZEOF_STRUCT_SOCKS4
];
343 struct socks4
*replyhead
= (struct socks4
*)buff
;
344 char *destdomname
= NULL
;
346 /* send socks header (target addr+port, +auth) */
347 #if WITH_MSGLEVEL <= E_INFO
348 if (ntohl(sockhead
->dest
) <= 0x000000ff) {
349 destdomname
= strchr(sockhead
->userid
, '\0')+1;
351 Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s",
353 sockhead
->version
, sockhead
->action
, ntohs(sockhead
->port
),
354 ((unsigned char *)&sockhead
->dest
)[0],
355 ((unsigned char *)&sockhead
->dest
)[1],
356 ((unsigned char *)&sockhead
->dest
)[2],
357 ((unsigned char *)&sockhead
->dest
)[3],
359 destdomname
?" DESTNAME=":"",
360 destdomname
?destdomname
:"");
361 #endif /* WITH_MSGLEVEL <= E_INFO */
362 #if WITH_MSGLEVEL <= E_DEBUG
365 if ((msgbuff
= Malloc(3*headlen
)) != NULL
) {
366 xiohexdump((const unsigned char *)sockhead
, headlen
, msgbuff
);
367 Debug1("sending socks4(a) request data %s", msgbuff
);
370 #endif /* WITH_MSGLEVEL <= E_DEBUG */
371 if (writefull(sfd
->fd
, sockhead
, headlen
) < 0) {
372 Msg4(level
, "write(%d, %p, "F_Zu
"): %s",
373 sfd
->fd
, sockhead
, headlen
, strerror(errno
));
374 if (Close(sfd
->fd
) < 0) {
375 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
377 return STAT_RETRYLATER
; /* retry complete open cycle */
381 Info("waiting for socks reply");
382 while (bytes
>= 0) { /* loop over answer chunks until complete or error */
383 /* receive socks answer */
385 result
= Read(sfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
);
386 } while (result
< 0 && errno
== EINTR
);
388 Msg4(level
, "read(%d, %p, "F_Zu
"): %s",
389 sfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
,
391 if (Close(sfd
->fd
) < 0) {
392 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
396 Msg(level
, "read(): EOF during read of socks reply, peer might not be a socks4 server");
397 if (Close(sfd
->fd
) < 0) {
398 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
400 return STAT_RETRYLATER
;
402 #if WITH_MSGLEVEL <= E_DEBUG
404 char msgbuff
[3*SIZEOF_STRUCT_SOCKS4
];
405 * xiohexdump((const unsigned char *)replyhead
+bytes
, result
, msgbuff
)
407 Debug2("received socks4 reply data (offset "F_Zd
"): %s", bytes
, msgbuff
);
409 #endif /* WITH_MSGLEVEL <= E_DEBUG */
411 if (bytes
== SIZEOF_STRUCT_SOCKS4
) {
412 Debug1("received all "F_Zd
" bytes", bytes
);
415 Debug2("received %d bytes, waiting for "F_Zu
" more bytes",
416 result
, SIZEOF_STRUCT_SOCKS4
-bytes
);
418 if (result
<= 0) { /* we had a problem while reading socks answer */
419 return STAT_RETRYLATER
; /* retry complete open cycle */
422 Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u",
423 replyhead
->version
, replyhead
->action
, ntohs(replyhead
->port
),
424 ((uint8_t *)&replyhead
->dest
)[0],
425 ((uint8_t *)&replyhead
->dest
)[1],
426 ((uint8_t *)&replyhead
->dest
)[2],
427 ((uint8_t *)&replyhead
->dest
)[3]);
428 if (replyhead
->version
!= 0) {
429 Warn1("socks: reply code version is not 0 (%d)",
433 switch (replyhead
->action
) {
434 case SOCKS_CD_GRANTED
:
435 /* Notice("socks: connect request succeeded"); */
437 if (Getsockname(sfd
->fd
, (struct sockaddr
*)&us
, &uslen
) < 0) {
438 Warn4("getsockname(%d, %p, {%d}): %s",
439 sfd
->fd
, &us
, uslen
, strerror(errno
));
441 Notice1("successfully connected from %s via socks4",
442 sockaddr_info((struct sockaddr
*)&us
, infobuff
, sizeof(infobuff
)));
444 Notice("successfully connected via socks4");
448 case SOCKS_CD_FAILED
:
449 Msg(level
, "socks: connect request rejected or failed");
450 return STAT_RETRYLATER
;
452 case SOCKS_CD_NOIDENT
:
453 Msg(level
, "socks: ident refused by client");
454 return STAT_RETRYLATER
;
456 case SOCKS_CD_IDENTFAILED
:
457 Msg(level
, "socks: ident failed");
458 return STAT_RETRYLATER
;
461 Msg1(level
, "socks: undefined status %u", replyhead
->action
);
466 #endif /* WITH_SOCKS4 || WITH_SOCKS4A */