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_SOCKS
, PH_LATE
, TYPE_STRING
, OFUNC_SPEC
};
33 const struct optdesc opt_socksuser
= { "socksuser", NULL
, OPT_SOCKSUSER
, GROUP_IP_SOCKS
, 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_SOCKS
|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_SOCKS
|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
**themarr
, *themp
;
60 bool needbind
= false;
63 unsigned char buff
[BUFF_LEN
];
64 struct socks4
*sockhead
= (struct socks4
*)buff
;
65 size_t buflen
= sizeof(buff
);
66 int socktype
= SOCK_STREAM
;
71 xio_syntax(argv
[0], 3, argc
-1, addrdesc
->syntax
);
78 if (sfd
->howtoend
== END_UNSPEC
)
79 sfd
->howtoend
= END_SHUTDOWN
;
80 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0) return -1;
81 applyopts(sfd
, 1, opts
, PH_INIT
);
83 retropt_int(opts
, OPT_SO_TYPE
, &socktype
);
85 retropt_bool(opts
, OPT_FORK
, &dofork
);
87 result
= _xioopen_socks4_prepare(targetport
, opts
, &socksport
, sockhead
, &buflen
);
88 if (result
!= STAT_OK
)
91 Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
93 ntohs(sockhead
->port
),
94 sockdname
, socksport
, sockhead
->userid
);
97 do { /* loop over retries (failed connect and socks-request attempts) */
102 _xioopen_ipapp_prepare(opts
, &opts0
, sockdname
, socksport
,
104 sfd
->para
.socket
.ip
.ai_flags
,
105 &themarr
, us
, &uslen
,
106 &needbind
, &lowport
, socktype
);
108 /* we try to resolve the target address _before_ connecting to the socks
109 server: this avoids unnecessary socks connects and timeouts */
111 _xioopen_socks4_connect0(sfd
, targetname
, socks4a
, sockhead
,
112 (ssize_t
*)&buflen
, level
);
116 case STAT_RETRYLATER
:
118 if (sfd
->forever
|| sfd
->retry
--) {
119 if (result
== STAT_RETRYLATER
)
120 Nanosleep(&sfd
->intervall
, NULL
);
123 #endif /* WITH_RETRY */
128 /* loop over themarr */
130 themp
= themarr
[i
++];
131 while (themp
!= NULL
) {
132 Notice1("opening connection to %s",
133 sockaddr_info(themp
->ai_addr
, themp
->ai_addrlen
,
134 infobuff
, sizeof(infobuff
)));
136 if (sfd
->forever
|| sfd
->retry
|| themarr
[i
] != NULL
) {
139 #endif /* WITH_RETRY */
142 /* this cannot fork because we retrieved fork option above */
144 _xioopen_connect(sfd
,
145 needbind
?us
:NULL
, uslen
,
146 themp
->ai_addr
, themp
->ai_addrlen
,
147 opts
, pf
?pf
:themp
->ai_family
, socktype
, IPPROTO_TCP
, lowport
, level
);
148 if (result
== STAT_OK
)
150 themp
= themarr
[i
++];
152 result
= STAT_RETRYLATER
;
157 case STAT_RETRYLATER
:
159 if (sfd
->forever
|| sfd
->retry
) {
161 if (result
== STAT_RETRYLATER
)
162 Nanosleep(&sfd
->intervall
, NULL
);
165 #endif /* WITH_RETRY */
167 xiofreeaddrinfo(themarr
);
170 xiofreeaddrinfo(themarr
);
171 applyopts(sfd
, -1, opts
, PH_ALL
);
173 if ((result
= _xio_openlate(sfd
, opts
)) < 0)
176 result
= _xioopen_socks4_connect(sfd
, sockhead
, buflen
, level
);
180 case STAT_RETRYLATER
:
182 if (sfd
->forever
|| sfd
->retry
--) {
183 if (result
== STAT_RETRYLATER
) Nanosleep(&sfd
->intervall
, NULL
);
186 #endif /* WITH_RETRY */
192 xiosetchilddied(); /* set SIGCHLD handler */
199 if (sfd
->forever
|| sfd
->retry
) {
200 level
= E_WARN
; /* most users won't expect a problem here,
201 so Notice is too weak */
203 while ((pid
= xio_fork(false, level
, sfd
->shutup
)) < 0) {
204 if (sfd
->forever
|| --sfd
->retry
) {
205 Nanosleep(&sfd
->intervall
, NULL
);
208 return STAT_RETRYLATER
;
211 if (pid
== 0) { /* child process */
212 sfd
->forever
= false;
219 Nanosleep(&sfd
->intervall
, NULL
);
220 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
223 #endif /* WITH_RETRY */
228 } while (true); /* end of complete open loop - drop out on success */
233 int _xioopen_opt_socksport(
239 if (retropt_string(opts
, OPT_SOCKSPORT
, socksport
) < 0) {
240 if ((se
= getservbyname("socks", "tcp")) != NULL
) {
241 Debug1("\"socks/tcp\" resolves to %u", ntohs(se
->s_port
));
242 if ((*socksport
= Malloc(6)) == NULL
) {
245 sprintf(*socksport
, "%u", ntohs(se
->s_port
));
247 Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT
);
248 if ((*socksport
= strdup(SOCKSPORT
)) == NULL
) {
257 int _xioopen_socks4_prepare(const char *targetport
, struct opt
*opts
, char **socksport
, struct socks4
*sockhead
, size_t *headlen
) {
260 /* generate socks header - points to final target */
261 sockhead
->version
= 4;
262 sockhead
->action
= 1;
263 sockhead
->port
= parseport(targetport
, IPPROTO_TCP
); /* network byte
265 if (_xioopen_opt_socksport(opts
, socksport
) < 0) {
269 if (retropt_string(opts
, OPT_SOCKSUSER
, &userid
) < 0) {
270 if ((userid
= getenv("LOGNAME")) == NULL
) {
271 if ((userid
= getenv("USER")) == NULL
) {
272 userid
= "anonymous";
276 sockhead
->userid
[0] = '\0'; strncat(sockhead
->userid
, userid
, *headlen
-SIZEOF_STRUCT_SOCKS4
-1);
277 *headlen
= SIZEOF_STRUCT_SOCKS4
+strlen(userid
)+1;
282 /* called within retry/fork loop, before connect() */
284 _xioopen_socks4_connect0(struct single
*sfd
,
285 const char *hostname
, /* socks target host */
287 struct socks4
*sockhead
,
288 ssize_t
*headlen
, /* get available space,
294 union sockaddr_union sau
;
295 socklen_t saulen
= sizeof(sau
);
297 if ((result
= xioresolve(hostname
, NULL
,
298 PF_INET
, SOCK_STREAM
, IPPROTO_TCP
,
300 sfd
->para
.socket
.ip
.ai_flags
))
302 return result
; /*! STAT_RETRY? */
304 memcpy(&sockhead
->dest
, &sau
.ip4
.sin_addr
, 4);
309 sockhead
->dest
= htonl(0x00000001); /* three bytes zero */
311 #endif /* WITH_SOCKS4A */
314 /* SOCKS4A requires us to append the host name to resolve
315 after the user name's trailing 0 byte. */
316 char* insert_position
= (char*) sockhead
+ *headlen
;
318 insert_position
[0] = '\0'; strncat(insert_position
, hostname
, BUFF_LEN
-*headlen
-1);
319 ((char *)sockhead
)[BUFF_LEN
-1] = 0;
320 *headlen
+= strlen(hostname
) + 1;
321 if (*headlen
> BUFF_LEN
) {
325 #endif /* WITH_SOCKS4A */
330 /* perform socks4 client dialog on existing FD.
331 Called within fork/retry loop, after connect() */
332 int _xioopen_socks4_connect(struct single
*sfd
,
333 struct socks4
*sockhead
,
338 unsigned char buff
[SIZEOF_STRUCT_SOCKS4
];
339 struct socks4
*replyhead
= (struct socks4
*)buff
;
340 char *destdomname
= NULL
;
342 /* send socks header (target addr+port, +auth) */
343 #if WITH_MSGLEVEL <= E_INFO
344 if (ntohl(sockhead
->dest
) <= 0x000000ff) {
345 destdomname
= strchr(sockhead
->userid
, '\0')+1;
347 Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s",
349 sockhead
->version
, sockhead
->action
, ntohs(sockhead
->port
),
350 ((unsigned char *)&sockhead
->dest
)[0],
351 ((unsigned char *)&sockhead
->dest
)[1],
352 ((unsigned char *)&sockhead
->dest
)[2],
353 ((unsigned char *)&sockhead
->dest
)[3],
355 destdomname
?" DESTNAME=":"",
356 destdomname
?destdomname
:"");
357 #endif /* WITH_MSGLEVEL <= E_INFO */
358 #if WITH_MSGLEVEL <= E_DEBUG
361 if ((msgbuff
= Malloc(3*headlen
)) != NULL
) {
362 xiohexdump((const unsigned char *)sockhead
, headlen
, msgbuff
);
363 Debug1("sending socks4(a) request data %s", msgbuff
);
366 #endif /* WITH_MSGLEVEL <= E_DEBUG */
367 if (writefull(sfd
->fd
, sockhead
, headlen
) < 0) {
368 Msg4(level
, "write(%d, %p, "F_Zu
"): %s",
369 sfd
->fd
, sockhead
, headlen
, strerror(errno
));
370 if (Close(sfd
->fd
) < 0) {
371 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
373 return STAT_RETRYLATER
; /* retry complete open cycle */
377 Info("waiting for socks reply");
378 while (bytes
>= 0) { /* loop over answer chunks until complete or error */
379 /* receive socks answer */
381 result
= Read(sfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
);
382 } while (result
< 0 && errno
== EINTR
);
384 Msg4(level
, "read(%d, %p, "F_Zu
"): %s",
385 sfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
,
387 if (Close(sfd
->fd
) < 0) {
388 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
392 Msg(level
, "read(): EOF during read of socks reply, peer might not be a socks4 server");
393 if (Close(sfd
->fd
) < 0) {
394 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
396 return STAT_RETRYLATER
;
398 #if WITH_MSGLEVEL <= E_DEBUG
400 char msgbuff
[3*SIZEOF_STRUCT_SOCKS4
];
401 * xiohexdump((const unsigned char *)replyhead
+bytes
, result
, msgbuff
)
403 Debug2("received socks4 reply data (offset "F_Zd
"): %s", bytes
, msgbuff
);
405 #endif /* WITH_MSGLEVEL <= E_DEBUG */
407 if (bytes
== SIZEOF_STRUCT_SOCKS4
) {
408 Debug1("received all "F_Zd
" bytes", bytes
);
411 Debug2("received %d bytes, waiting for "F_Zu
" more bytes",
412 result
, SIZEOF_STRUCT_SOCKS4
-bytes
);
414 if (result
<= 0) { /* we had a problem while reading socks answer */
415 return STAT_RETRYLATER
; /* retry complete open cycle */
418 Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u",
419 replyhead
->version
, replyhead
->action
, ntohs(replyhead
->port
),
420 ((uint8_t *)&replyhead
->dest
)[0],
421 ((uint8_t *)&replyhead
->dest
)[1],
422 ((uint8_t *)&replyhead
->dest
)[2],
423 ((uint8_t *)&replyhead
->dest
)[3]);
424 if (replyhead
->version
!= 0) {
425 Warn1("socks: reply code version is not 0 (%d)",
429 switch (replyhead
->action
) {
430 case SOCKS_CD_GRANTED
:
431 /* Notice("socks: connect request succeeded"); */
433 if (Getsockname(sfd
->fd
, (struct sockaddr
*)&us
, &uslen
) < 0) {
434 Warn4("getsockname(%d, %p, {%d}): %s",
435 sfd
->fd
, &us
, uslen
, strerror(errno
));
437 Notice1("successfully connected from %s via socks4",
438 sockaddr_info((struct sockaddr
*)&us
, infobuff
, sizeof(infobuff
)));
440 Notice("successfully connected via socks4");
444 case SOCKS_CD_FAILED
:
445 Msg(level
, "socks: connect request rejected or failed");
446 return STAT_RETRYLATER
;
448 case SOCKS_CD_NOIDENT
:
449 Msg(level
, "socks: ident refused by client");
450 return STAT_RETRYLATER
;
452 case SOCKS_CD_IDENTFAILED
:
453 Msg(level
, "socks: ident failed");
454 return STAT_RETRYLATER
;
457 Msg1(level
, "socks: undefined status %u", replyhead
->action
);
462 #endif /* WITH_SOCKS4 || WITH_SOCKS4A */