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
,
31 int xioflags
, xiofile_t
*fd
,
32 unsigned groups
, int dummy1
, int dummy2
,
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
,
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
;
52 int ipproto
= IPPROTO_TCP
;
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;
60 unsigned char buff
[BUFF_LEN
];
61 struct socks4
*sockhead
= (struct socks4
*)buff
;
62 size_t buflen
= sizeof(buff
);
63 int socktype
= SOCK_STREAM
;
68 Error1("%s: 3 parameters required", argv
[0]);
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
;
86 _xioopen_ipapp_prepare(opts
, &opts0
, sockdname
, socksport
,
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\"",
95 ntohs(sockhead
->port
),
96 sockdname
, socksport
, sockhead
->userid
);
98 do { /* loop over failed connect and socks-request attempts */
101 if (xfd
->forever
|| xfd
->retry
) {
104 #endif /* WITH_RETRY */
107 /* we try to resolve the target address _before_ connecting to the socks
108 server: this avoids unnecessary socks connects and timeouts */
110 _xioopen_socks4_connect0(xfd
, targetname
, socks4a
, sockhead
,
111 (ssize_t
*)&buflen
, level
);
115 case STAT_RETRYLATER
:
117 if (xfd
->forever
|| xfd
->retry
--) {
118 if (result
== STAT_RETRYLATER
) Nanosleep(&xfd
->intervall
, NULL
);
121 #endif /* WITH_RETRY */
126 /* this cannot fork because we retrieved fork option above */
128 _xioopen_connect (xfd
,
129 needbind
?us
:NULL
, sizeof(*us
),
130 (struct sockaddr
*)them
, themlen
,
131 opts
, pf
, socktype
, IPPROTO_TCP
, lowport
, level
);
135 case STAT_RETRYLATER
:
137 if (xfd
->forever
|| xfd
->retry
--) {
138 if (result
== STAT_RETRYLATER
) Nanosleep(&xfd
->intervall
, NULL
);
141 #endif /* WITH_RETRY */
146 applyopts(xfd
->fd
, opts
, PH_ALL
);
148 if ((result
= _xio_openlate(xfd
, opts
)) < 0)
151 result
= _xioopen_socks4_connect(xfd
, sockhead
, buflen
, level
);
155 case STAT_RETRYLATER
:
157 if (xfd
->forever
|| xfd
->retry
--) {
158 if (result
== STAT_RETRYLATER
) Nanosleep(&xfd
->intervall
, NULL
);
161 #endif /* WITH_RETRY */
167 xiosetchilddied(); /* set SIGCHLD handler */
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
);
183 return STAT_RETRYLATER
;
186 if (pid
== 0) { /* child process */
187 xfd
->forever
= false; xfd
->retry
= 0;
193 Nanosleep(&xfd
->intervall
, NULL
);
194 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
197 #endif /* WITH_RETRY */
202 } while (true); /* end of complete open loop - drop out on success */
207 int _xioopen_socks4_prepare(const char *targetport
, struct opt
*opts
, char **socksport
, struct socks4
*sockhead
, size_t *headlen
) {
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
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
) {
223 sprintf(*socksport
, "%u", ntohs(se
->s_port
));
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;
245 /* called within retry/fork loop, before connect() */
247 _xioopen_socks4_connect0(struct single
*xfd
,
248 const char *hostname
, /* socks target host */
250 struct socks4
*sockhead
,
251 ssize_t
*headlen
, /* get available space,
257 union sockaddr_union sau
;
258 socklen_t saulen
= sizeof(sau
);
260 if ((result
= xiogetaddrinfo(hostname
, NULL
,
261 PF_INET
, SOCK_STREAM
, IPPROTO_TCP
,
263 xfd
->para
.socket
.ip
.res_opts
[1],
264 xfd
->para
.socket
.ip
.res_opts
[0]))
266 return result
; /*! STAT_RETRY? */
268 memcpy(&sockhead
->dest
, &sau
.ip4
.sin_addr
, 4);
273 sockhead
->dest
= htonl(0x00000001); /* three bytes zero */
275 #endif /* WITH_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
) {
289 #endif /* WITH_SOCKS4A */
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
,
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",
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],
319 destdomname
?" DESTNAME=":"",
320 destdomname
?destdomname
:"");
321 #endif /* WITH_MSGLEVEL <= E_INFO */
322 #if WITH_MSGLEVEL <= E_DEBUG
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 */
341 Info("waiting for socks reply");
342 while (bytes
>= 0) { /* loop over answer chunks until complete or error */
343 /* receive socks answer */
345 result
= Read(xfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
);
346 } while (result
< 0 && errno
== EINTR
);
348 Msg4(level
, "read(%d, %p, "F_Zu
"): %s",
349 xfd
->fd
, buff
+bytes
, SIZEOF_STRUCT_SOCKS4
-bytes
,
351 if (Close(xfd
->fd
) < 0) {
352 Info2("close(%d): %s", xfd
->fd
, strerror(errno
));
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
)
367 Debug2("received socks4 reply data (offset "F_Zd
"): %s", bytes
, msgbuff
);
369 #endif /* WITH_MSGLEVEL <= E_DEBUG */
371 if (bytes
== SIZEOF_STRUCT_SOCKS4
) {
372 Debug1("received all "F_Zd
" bytes", bytes
);
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)",
393 switch (replyhead
->action
) {
394 case SOCKS_CD_GRANTED
:
395 /* Notice("socks: connect request succeeded"); */
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
)));
404 Notice("successfully connected via socks4");
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
;
421 Msg1(level
, "socks: undefined status %u", replyhead
->action
);
426 #endif /* WITH_SOCKS4 || WITH_SOCKS4A */