Line data Source code
1 : /* socks5.c - Check the SOCKS5 client feature
2 : * Copyright (C) 2015 g10 Code GmbH
3 : *
4 : * This file is part of Assuan.
5 : *
6 : * Assuan is free software; you can redistribute it and/or modify it
7 : * under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * Assuan is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #ifdef HAVE_CONFIG_H
21 : #include <config.h>
22 : #endif
23 :
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <assert.h>
28 : #ifdef HAVE_W32_SYSTEM
29 : # ifdef HAVE_WINSOCK2_H
30 : # include <winsock2.h>
31 : # endif
32 : # include <windows.h>
33 : #else /*!HAVE_W32_SYSTEM*/
34 : # include <sys/types.h>
35 : # include <sys/socket.h>
36 : # include <netdb.h>
37 : #endif /*!HAVE_W32_SYSTEM*/
38 :
39 : #include "../src/assuan.h"
40 : #include "common.h"
41 :
42 :
43 : /*
44 :
45 : M A I N
46 :
47 : */
48 : int
49 0 : main (int argc, char **argv)
50 : {
51 0 : int last_argc = -1;
52 : gpg_error_t err;
53 0 : int only_v6 = 0;
54 0 : int only_v4 = 0;
55 0 : int use_tor = 0;
56 0 : int opt_have_proxy = 0;
57 0 : int disable_socks = 0;
58 0 : int opt_byname = 0;
59 0 : const char *user = NULL;
60 0 : const char *pass = NULL;
61 0 : assuan_fd_t sock = ASSUAN_INVALID_FD;
62 : estream_t infp, outfp;
63 : int c;
64 : int lf_seen;
65 :
66 0 : gpgrt_init ();
67 0 : if (argc)
68 : {
69 0 : log_set_prefix (*argv);
70 0 : argc--; argv++;
71 : }
72 0 : while (argc && last_argc != argc )
73 : {
74 0 : last_argc = argc;
75 0 : if (!strcmp (*argv, "--"))
76 : {
77 0 : argc--; argv++;
78 0 : break;
79 : }
80 0 : else if (!strcmp (*argv, "--help"))
81 : {
82 0 : puts (
83 : "usage: ./socks5 [options] HOST PORT\n"
84 : "\n"
85 : "Options:\n"
86 : " --verbose Show what is going on\n"
87 : " --use-tor Use port 9050 instead of 1080\n"
88 : " --inet6-only Use only IPv6\n"
89 : " --inet4-only Use only IPv4\n"
90 : " --disable-socks Connect w/o SOCKS\n"
91 : " --have-proxy Check whether the proxy is available\n"
92 : " --byname Use assuan_sock_connect_byname\n"
93 : " --user STRING Use STRING as user for authentication\n"
94 : " --pass STRING Use STRING as password for authentication\n"
95 : );
96 0 : exit (0);
97 : }
98 0 : if (!strcmp (*argv, "--verbose"))
99 : {
100 0 : verbose = 1;
101 0 : argc--; argv++;
102 : }
103 0 : else if (!strcmp (*argv, "--debug"))
104 : {
105 0 : verbose = debug = 1;
106 0 : argc--; argv++;
107 : }
108 0 : else if (!strcmp (*argv, "-6") || !strcmp (*argv, "--inet6-only"))
109 : {
110 0 : only_v6 = 1;
111 0 : argc--; argv++;
112 : }
113 0 : else if (!strcmp (*argv, "-4") || !strcmp (*argv, "--inet4-only"))
114 : {
115 0 : only_v4 = 1;
116 0 : argc--; argv++;
117 : }
118 0 : else if (!strcmp (*argv, "--use-tor"))
119 : {
120 0 : use_tor = 1;
121 0 : argc--; argv++;
122 : }
123 0 : else if (!strcmp (*argv, "--disable-socks"))
124 : {
125 0 : disable_socks = 1;
126 0 : argc--; argv++;
127 : }
128 0 : else if (!strcmp (*argv, "--byname"))
129 : {
130 0 : opt_byname = 1;
131 0 : argc--; argv++;
132 : }
133 0 : else if (!strcmp (*argv, "--have-proxy"))
134 : {
135 0 : opt_have_proxy = 1;
136 0 : argc--; argv++;
137 : }
138 0 : else if (!strcmp (*argv, "--user"))
139 : {
140 0 : argc--; argv++;
141 0 : if (argc)
142 : {
143 0 : user = *argv;
144 0 : argc--; argv++;
145 : }
146 : }
147 0 : else if (!strcmp (*argv, "--pass"))
148 : {
149 0 : argc--; argv++;
150 0 : if (argc)
151 : {
152 0 : pass = *argv;
153 0 : argc--; argv++;
154 : }
155 : }
156 0 : else if (!strncmp (*argv, "--", 2))
157 : {
158 0 : log_error ("unknown option '%s'\n", *argv);
159 0 : exit (1);
160 : }
161 : }
162 :
163 0 : if (argc != 2 && !opt_have_proxy)
164 : {
165 0 : fputs ("usage: socks5 HOST PORT\n", stderr);
166 0 : exit (1);
167 : }
168 :
169 0 : assuan_set_assuan_log_prefix (log_prefix);
170 :
171 0 : if (!assuan_check_version (ASSUAN_VERSION))
172 0 : log_error ("assuan_check_version returned an error\n");
173 :
174 0 : assuan_sock_init ();
175 :
176 0 : if (!disable_socks
177 0 : && assuan_sock_set_flag (ASSUAN_INVALID_FD,
178 : use_tor? "tor-mode":"socks", 1))
179 : {
180 0 : err = gpg_error_from_syserror ();
181 0 : log_fatal ("setting %s mode failed: %s\n",
182 : use_tor? "TOR": "SOCKS", gpg_strerror (err));
183 : }
184 :
185 0 : if (opt_have_proxy)
186 : {
187 : char *cred;
188 :
189 0 : if (user || pass)
190 0 : cred = xstrconcat (user?user:"", ":", pass, NULL);
191 : else
192 0 : cred = NULL;
193 :
194 0 : sock = assuan_sock_connect_byname
195 : (NULL, 0, 0, cred, use_tor? ASSUAN_SOCK_TOR : ASSUAN_SOCK_SOCKS);
196 0 : if (sock == ASSUAN_INVALID_FD)
197 : {
198 0 : err = gpg_error_from_syserror ();
199 0 : log_error ("SOCKS proxy is not available (%s)\n", gpg_strerror (err));
200 0 : exit (1);
201 : }
202 0 : xfree (cred);
203 0 : assuan_sock_close (sock);
204 0 : if (verbose)
205 0 : log_info ("SOCKS proxy available\n");
206 0 : exit (0);
207 : }
208 0 : else if (opt_byname)
209 : {
210 : unsigned short port;
211 : char *cred;
212 :
213 0 : if (user || pass)
214 0 : cred = xstrconcat (user?user:"", ":", pass, NULL);
215 : else
216 0 : cred = NULL;
217 :
218 0 : port = strtoul (argv[1], NULL, 10);
219 : if (port < 0 || port > 65535)
220 : log_fatal ("port number out of range\n");
221 :
222 0 : sock = assuan_sock_connect_byname (argv[0], port, 0, cred,
223 : ASSUAN_SOCK_TOR);
224 0 : if (sock == ASSUAN_INVALID_FD)
225 : {
226 0 : err = gpg_error_from_syserror ();
227 0 : log_error ("assuan_sock_connect_byname (%s) failed: %s\n",
228 : argv[0], gpg_strerror (err));
229 0 : exit (1);
230 : }
231 0 : xfree (cred);
232 : }
233 : else
234 : {
235 : #ifdef HAVE_GETADDRINFO
236 : struct addrinfo hints, *res, *ai;
237 : int ret;
238 0 : int anyok = 0;
239 :
240 0 : memset (&hints, 0, sizeof (hints));
241 0 : hints.ai_socktype = SOCK_STREAM;
242 0 : ret = getaddrinfo (argv[0], argv[1], &hints, &res);
243 0 : if (ret)
244 : {
245 0 : log_error ("error resolving '%s': %s\n", argv[0], gai_strerror (ret));
246 0 : exit (1);
247 : }
248 :
249 0 : for (ai = res; ai; ai = ai->ai_next)
250 : {
251 0 : if (ai->ai_family == AF_INET && only_v6)
252 0 : continue;
253 0 : if (ai->ai_family == AF_INET6 && only_v4)
254 0 : continue;
255 :
256 0 : if (sock != ASSUAN_INVALID_FD)
257 0 : assuan_sock_close (sock);
258 0 : sock = assuan_sock_new (ai->ai_family, ai->ai_socktype,
259 : ai->ai_protocol);
260 0 : if (sock == ASSUAN_INVALID_FD)
261 : {
262 0 : err = gpg_error_from_syserror ();
263 0 : log_error ("error creating socket: %s\n", gpg_strerror (err));
264 0 : freeaddrinfo (res);
265 0 : exit (1);
266 : }
267 :
268 0 : if (assuan_sock_connect (sock, ai->ai_addr, ai->ai_addrlen))
269 : {
270 0 : err = gpg_error_from_syserror ();
271 0 : log_error ("assuan_sock_connect (%s) failed: %s\n",
272 0 : ai->ai_family == AF_INET6? "v6" :
273 0 : ai->ai_family == AF_INET ? "v4" : "?",
274 : gpg_strerror (err));
275 : }
276 : else
277 : {
278 0 : log_info ("assuan_sock_connect succeeded (%s)\n",
279 0 : ai->ai_family == AF_INET6? "v6" :
280 0 : ai->ai_family == AF_INET ? "v4" : "?");
281 0 : anyok = 1;
282 0 : break;
283 : }
284 : }
285 0 : freeaddrinfo (res);
286 0 : if (!anyok)
287 0 : exit (1);
288 : #else /*!HAVE_GETADDRINFO*/
289 : (void)only_v4;
290 : (void)only_v6;
291 : fputs ("socks5: getaddrinfo not supported\n", stderr);
292 : exit (77); /* Skip test. */
293 : #endif /*!HAVE_GETADDRINFO*/
294 : }
295 :
296 0 : infp = es_fdopen_nc (sock, "rb");
297 0 : if (!infp)
298 : {
299 0 : err = gpg_error_from_syserror ();
300 0 : assuan_sock_close (sock);
301 0 : log_fatal ("opening inbound stream failed: %s\n", gpg_strerror (err));
302 : }
303 0 : outfp = es_fdopen (sock, "wb");
304 0 : if (!outfp)
305 : {
306 0 : err = gpg_error_from_syserror ();
307 0 : es_fclose (infp);
308 0 : assuan_sock_close (sock);
309 0 : log_fatal ("opening outbound stream failed: %s\n", gpg_strerror (err));
310 : }
311 0 : es_fputs ("GET / HTTP/1.0\r\n\r\n", outfp);
312 0 : if (es_fflush (outfp))
313 : {
314 0 : err = gpg_error_from_syserror ();
315 0 : log_error ("es_fflush failed: %s\n", gpg_strerror (err));
316 : }
317 0 : lf_seen = 0;
318 0 : while ((c = es_fgetc (infp)) != EOF)
319 : {
320 0 : if (c == '\r')
321 0 : continue;
322 0 : putchar (c);
323 0 : if (c == '\n')
324 : {
325 0 : if (lf_seen)
326 0 : break;
327 0 : lf_seen = 1;
328 : }
329 : else
330 0 : lf_seen = 0;
331 : }
332 0 : if (es_ferror (infp))
333 : {
334 0 : err = gpg_error_from_syserror ();
335 0 : log_error ("es_fgetc failed: %s\n", gpg_strerror (err));
336 : }
337 0 : es_fclose (infp);
338 0 : es_fclose (outfp);
339 :
340 0 : return errorcount ? 1 : 0;
341 : }
|