1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * Socks5 Client -related helpers
25 */
26
27#include "private-lib-core.h"
28
29int
30lws_set_socks(struct lws_vhost *vhost, const char *socks)
31{
32	char *p_at, *p_colon;
33	char user[96];
34	char password[96];
35
36	if (!socks)
37		return -1;
38
39	vhost->socks_user[0] = '\0';
40	vhost->socks_password[0] = '\0';
41
42	p_at = strrchr(socks, '@');
43	if (p_at) { /* auth is around */
44		if (lws_ptr_diff_size_t(p_at, socks) > (sizeof(user) +
45							sizeof(password) - 2)) {
46			lwsl_vhost_err(vhost, "auth too long");
47			goto bail;
48		}
49
50		p_colon = strchr(socks, ':');
51		if (p_colon) {
52			if (lws_ptr_diff_size_t(p_colon, socks) >
53							     sizeof(user) - 1) {
54				lwsl_vhost_err(vhost, "user too long");
55				goto bail;
56			}
57			if (lws_ptr_diff_size_t(p_at, p_colon) >
58						         sizeof(password) - 1) {
59				lwsl_vhost_err(vhost, "pw too long");
60				goto bail;
61			}
62
63			lws_strncpy(vhost->socks_user, socks,
64				    lws_ptr_diff_size_t(p_colon, socks) + 1);
65			lws_strncpy(vhost->socks_password, p_colon + 1,
66				lws_ptr_diff_size_t(p_at, (p_colon + 1)) + 1);
67		}
68
69		lwsl_vhost_info(vhost, " Socks auth, user: %s, password: %s",
70				       vhost->socks_user,
71				       vhost->socks_password);
72
73		socks = p_at + 1;
74	}
75
76	lws_strncpy(vhost->socks_proxy_address, socks,
77		    sizeof(vhost->socks_proxy_address));
78
79	p_colon = strchr(vhost->socks_proxy_address, ':');
80	if (!p_colon && !vhost->socks_proxy_port) {
81		lwsl_vhost_err(vhost, "socks_proxy needs to be address:port");
82
83		return -1;
84	}
85
86	if (p_colon) {
87		*p_colon = '\0';
88		vhost->socks_proxy_port = (unsigned int)atoi(p_colon + 1);
89	}
90
91	lwsl_vhost_debug(vhost, "Connections via Socks5 %s:%u",
92				vhost->socks_proxy_address,
93				vhost->socks_proxy_port);
94
95	return 0;
96
97bail:
98	return -1;
99}
100
101int
102lws_socks5c_generate_msg(struct lws *wsi, enum socks_msg_type type,
103			 ssize_t *msg_len)
104{
105	struct lws_context *context = wsi->a.context;
106	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
107	uint8_t *p = pt->serv_buf, *end = &p[context->pt_serv_buf_size];
108	ssize_t n, passwd_len;
109	short net_num;
110	char *cp;
111
112	switch (type) {
113	case SOCKS_MSG_GREETING:
114		if (lws_ptr_diff(end, p) < 4)
115			return 1;
116		/* socks version, version 5 only */
117		*p++ = SOCKS_VERSION_5;
118		/* number of methods */
119		*p++ = 2;
120		/* username password method */
121		*p++ = SOCKS_AUTH_USERNAME_PASSWORD;
122		/* no authentication method */
123		*p++ = SOCKS_AUTH_NO_AUTH;
124		break;
125
126	case SOCKS_MSG_USERNAME_PASSWORD:
127		n = (ssize_t)strlen(wsi->a.vhost->socks_user);
128		passwd_len = (ssize_t)strlen(wsi->a.vhost->socks_password);
129
130		if (n > 254 || passwd_len > 254)
131			return 1;
132
133		if (lws_ptr_diff(end, p) < 3 + n + passwd_len)
134			return 1;
135
136		/* the subnegotiation version */
137		*p++ = SOCKS_SUBNEGOTIATION_VERSION_1;
138
139		/* length of the user name */
140		*p++ = (uint8_t)n;
141		/* user name */
142		memcpy(p, wsi->a.vhost->socks_user, (size_t)n);
143		p += (uint8_t)n;
144
145		/* length of the password */
146		*p++ = (uint8_t)passwd_len;
147
148		/* password */
149		memcpy(p, wsi->a.vhost->socks_password, (size_t)passwd_len);
150		p += passwd_len;
151		break;
152
153	case SOCKS_MSG_CONNECT:
154		n = (ssize_t)strlen(wsi->stash->cis[CIS_ADDRESS]);
155
156		if (n > 254 || lws_ptr_diff(end, p) < 5 + n + 2)
157			return 1;
158
159		cp = (char *)&net_num;
160
161		/* socks version */
162		*p++ = SOCKS_VERSION_5;
163		/* socks command */
164		*p++ = SOCKS_COMMAND_CONNECT;
165		/* reserved */
166		*p++ = 0;
167		/* address type */
168		*p++ = SOCKS_ATYP_DOMAINNAME;
169		/* length of ---> */
170		*p++ = (uint8_t)n;
171
172		/* the address we tell SOCKS proxy to connect to */
173		memcpy(p, wsi->stash->cis[CIS_ADDRESS], (size_t)n);
174		p += n;
175
176		net_num = (short)htons(wsi->c_port);
177
178		/* the port we tell SOCKS proxy to connect to */
179		*p++ = (uint8_t)cp[0];
180		*p++ = (uint8_t)cp[1];
181
182		break;
183
184	default:
185		return 1;
186	}
187
188	*msg_len = lws_ptr_diff(p, pt->serv_buf);
189
190	return 0;
191}
192
193int
194lws_socks5c_ads_server(struct lws_vhost *vh,
195		      const struct lws_context_creation_info *info)
196{
197	/* socks proxy */
198	if (info->socks_proxy_address) {
199		/* override for backwards compatibility */
200		if (info->socks_proxy_port)
201			vh->socks_proxy_port = info->socks_proxy_port;
202		lws_set_socks(vh, info->socks_proxy_address);
203
204		return 0;
205	}
206#ifdef LWS_HAVE_GETENV
207	{
208		char *p = getenv("socks_proxy");
209
210		if (p && strlen(p) > 0 && strlen(p) < 95)
211			lws_set_socks(vh, p);
212	}
213#endif
214
215	return 0;
216}
217
218/*
219 * Returns 0 = nothing for caller to do, 1 = return wsi, -1 = goto failed
220 */
221
222int
223lws_socks5c_greet(struct lws *wsi, const char **pcce)
224{
225	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
226	ssize_t plen;
227	int n;
228
229	/* socks proxy */
230	if (!wsi->a.vhost->socks_proxy_port)
231		return 0;
232
233	if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) {
234		*pcce = "socks msg too large";
235		return -1;
236	}
237	// lwsl_hexdump_notice(pt->serv_buf, plen);
238	n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, (size_t)plen,
239		      MSG_NOSIGNAL);
240	if (n < 0) {
241		lwsl_wsi_debug(wsi, "ERROR writing socks greeting");
242		*pcce = "socks write failed";
243		return -1;
244	}
245
246	lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
247			(int)wsi->a.context->timeout_secs);
248
249	lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
250
251	return 1;
252}
253
254int
255lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd,
256			 const char **pcce)
257{
258	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
259	int conn_mode = 0, pending_timeout = 0;
260	ssize_t len;
261	int n;
262
263	/* handle proxy hung up on us */
264
265	if (pollfd->revents & LWS_POLLHUP) {
266		lwsl_wsi_warn(wsi, "SOCKS fd=%d dead", pollfd->fd);
267		*pcce = "socks conn dead";
268		return LW5CHS_RET_BAIL3;
269	}
270
271	n = (int)recv(wsi->desc.sockfd, (void *)pt->serv_buf,
272		 wsi->a.context->pt_serv_buf_size, 0);
273	if (n < 0) {
274		if (LWS_ERRNO == LWS_EAGAIN) {
275			lwsl_wsi_debug(wsi, "SOCKS read EAGAIN, retrying");
276			return LW5CHS_RET_RET0;
277		}
278		lwsl_wsi_err(wsi, "ERROR reading from SOCKS socket");
279		*pcce = "socks recv fail";
280		return LW5CHS_RET_BAIL3;
281	}
282
283	// lwsl_hexdump_warn(pt->serv_buf, n);
284
285	switch (lwsi_state(wsi)) {
286
287	case LRS_WAITING_SOCKS_GREETING_REPLY:
288		if (pt->serv_buf[0] != SOCKS_VERSION_5)
289			goto socks_reply_fail;
290
291		if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
292			lwsl_wsi_client(wsi, "SOCKS GR: No Auth Method");
293			if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT,
294						     &len)) {
295				lwsl_wsi_err(wsi, "generate connect msg fail");
296				goto socks_send_msg_fail;
297			}
298			conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
299			pending_timeout =
300			   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
301			goto socks_send;
302		}
303
304		if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
305			lwsl_wsi_client(wsi, "SOCKS GR: User/Pw Method");
306			if (lws_socks5c_generate_msg(wsi,
307					   SOCKS_MSG_USERNAME_PASSWORD,
308					   &len))
309				goto socks_send_msg_fail;
310			conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
311			pending_timeout =
312			      PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
313			goto socks_send;
314		}
315		goto socks_reply_fail;
316
317	case LRS_WAITING_SOCKS_AUTH_REPLY:
318		if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
319		    pt->serv_buf[1] !=
320				    SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
321			goto socks_reply_fail;
322
323		lwsl_wsi_client(wsi, "SOCKS password OK, sending connect");
324		if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) {
325socks_send_msg_fail:
326			*pcce = "socks gen msg fail";
327			return LW5CHS_RET_BAIL3;
328		}
329		conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
330		pending_timeout =
331			   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
332socks_send:
333		// lwsl_hexdump_notice(pt->serv_buf, len);
334		n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf,
335			      (size_t)len, MSG_NOSIGNAL);
336		if (n < 0) {
337			lwsl_wsi_debug(wsi, "ERROR writing to socks proxy");
338			*pcce = "socks write fail";
339			return LW5CHS_RET_BAIL3;
340		}
341
342		lws_set_timeout(wsi, (enum pending_timeout)pending_timeout,
343				(int)wsi->a.context->timeout_secs);
344		lwsi_set_state(wsi, (lws_wsi_state_t)conn_mode);
345		break;
346
347socks_reply_fail:
348		lwsl_wsi_err(wsi, "socks reply: v%d, err %d",
349			     pt->serv_buf[0], pt->serv_buf[1]);
350		*pcce = "socks reply fail";
351		return LW5CHS_RET_BAIL3;
352
353	case LRS_WAITING_SOCKS_CONNECT_REPLY:
354		if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
355		    pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
356			goto socks_reply_fail;
357
358		lwsl_wsi_client(wsi, "socks connect OK");
359
360#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
361		if (lwsi_role_http(wsi) &&
362		    lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
363					  wsi->a.vhost->socks_proxy_address)) {
364			*pcce = "socks connect fail";
365			return LW5CHS_RET_BAIL3;
366		}
367#endif
368
369		wsi->c_port = (uint16_t)wsi->a.vhost->socks_proxy_port;
370
371		/* clear his proxy connection timeout */
372		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
373		return LW5CHS_RET_STARTHS;
374	default:
375		break;
376	}
377
378	return LW5CHS_RET_NOTHING;
379}
380