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
29 int
lws_set_socks(struct lws_vhost * vhost,const char * socks)30 lws_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 ((unsigned int)(p_at - socks) > (sizeof(user)
45 + sizeof(password) - 2)) {
46 lwsl_err("Socks auth too long\n");
47 goto bail;
48 }
49
50 p_colon = strchr(socks, ':');
51 if (p_colon) {
52 if ((unsigned int)(p_colon - socks) > (sizeof(user)
53 - 1) ) {
54 lwsl_err("Socks user too long\n");
55 goto bail;
56 }
57 if ((unsigned int)(p_at - p_colon) > (sizeof(password)
58 - 1) ) {
59 lwsl_err("Socks password too long\n");
60 goto bail;
61 }
62
63 lws_strncpy(vhost->socks_user, socks,
64 p_colon - socks + 1);
65 lws_strncpy(vhost->socks_password, p_colon + 1,
66 p_at - (p_colon + 1) + 1);
67 }
68
69 lwsl_info(" Socks auth, user: %s, password: %s\n",
70 vhost->socks_user, vhost->socks_password );
71
72 socks = p_at + 1;
73 }
74
75 lws_strncpy(vhost->socks_proxy_address, socks,
76 sizeof(vhost->socks_proxy_address));
77
78 p_colon = strchr(vhost->socks_proxy_address, ':');
79 if (!p_colon && !vhost->socks_proxy_port) {
80 lwsl_err("socks_proxy needs to be address:port\n");
81 return -1;
82 } else {
83 if (p_colon) {
84 *p_colon = '\0';
85 vhost->socks_proxy_port = atoi(p_colon + 1);
86 }
87 }
88
89 lwsl_debug("%s: Connections via Socks5 %s:%u\n", __func__,
90 vhost->socks_proxy_address, vhost->socks_proxy_port);
91
92 return 0;
93
94 bail:
95 return -1;
96 }
97
98 int
lws_socks5c_generate_msg(struct lws * wsi,enum socks_msg_type type,ssize_t * msg_len)99 lws_socks5c_generate_msg(struct lws *wsi, enum socks_msg_type type,
100 ssize_t *msg_len)
101 {
102 struct lws_context *context = wsi->context;
103 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
104 uint8_t *p = pt->serv_buf, *end = &p[context->pt_serv_buf_size];
105 ssize_t n, passwd_len;
106 short net_num;
107 char *cp;
108
109 switch (type) {
110 case SOCKS_MSG_GREETING:
111 if (lws_ptr_diff(end, p) < 4)
112 return 1;
113 /* socks version, version 5 only */
114 *p++ = SOCKS_VERSION_5;
115 /* number of methods */
116 *p++ = 2;
117 /* username password method */
118 *p++ = SOCKS_AUTH_USERNAME_PASSWORD;
119 /* no authentication method */
120 *p++ = SOCKS_AUTH_NO_AUTH;
121 break;
122
123 case SOCKS_MSG_USERNAME_PASSWORD:
124 n = strlen(wsi->vhost->socks_user);
125 passwd_len = strlen(wsi->vhost->socks_password);
126
127 if (n > 254 || passwd_len > 254)
128 return 1;
129
130 if (lws_ptr_diff(end, p) < 3 + n + passwd_len)
131 return 1;
132
133 /* the subnegotiation version */
134 *p++ = SOCKS_SUBNEGOTIATION_VERSION_1;
135
136 /* length of the user name */
137 *p++ = n;
138 /* user name */
139 memcpy(p, wsi->vhost->socks_user, n);
140 p += n;
141
142 /* length of the password */
143 *p++ = passwd_len;
144
145 /* password */
146 memcpy(p, wsi->vhost->socks_password, passwd_len);
147 p += passwd_len;
148 break;
149
150 case SOCKS_MSG_CONNECT:
151 n = strlen(wsi->stash->cis[CIS_ADDRESS]);
152
153 if (n > 254 || lws_ptr_diff(end, p) < 5 + n + 2)
154 return 1;
155
156 cp = (char *)&net_num;
157
158 /* socks version */
159 *p++ = SOCKS_VERSION_5;
160 /* socks command */
161 *p++ = SOCKS_COMMAND_CONNECT;
162 /* reserved */
163 *p++ = 0;
164 /* address type */
165 *p++ = SOCKS_ATYP_DOMAINNAME;
166 /* length of ---> */
167 *p++ = n;
168
169 /* the address we tell SOCKS proxy to connect to */
170 memcpy(p, wsi->stash->cis[CIS_ADDRESS], n);
171 p += n;
172
173 net_num = htons(wsi->c_port);
174
175 /* the port we tell SOCKS proxy to connect to */
176 *p++ = cp[0];
177 *p++ = cp[1];
178
179 break;
180
181 default:
182 return 1;
183 }
184
185 *msg_len = lws_ptr_diff(p, pt->serv_buf);
186
187 return 0;
188 }
189
190 int
lws_socks5c_ads_server(struct lws_vhost * vh,const struct lws_context_creation_info * info)191 lws_socks5c_ads_server(struct lws_vhost *vh,
192 const struct lws_context_creation_info *info)
193 {
194 /* socks proxy */
195 if (info->socks_proxy_address) {
196 /* override for backwards compatibility */
197 if (info->socks_proxy_port)
198 vh->socks_proxy_port = info->socks_proxy_port;
199 lws_set_socks(vh, info->socks_proxy_address);
200
201 return 0;
202 }
203 #ifdef LWS_HAVE_GETENV
204 {
205 char *p = getenv("socks_proxy");
206
207 if (p && strlen(p) > 0 && strlen(p) < 95)
208 lws_set_socks(vh, p);
209 }
210 #endif
211
212 return 0;
213 }
214
215 /*
216 * Returns 0 = nothing for caller to do, 1 = return wsi, -1 = goto failed
217 */
218
219 int
lws_socks5c_greet(struct lws * wsi,const char ** pcce)220 lws_socks5c_greet(struct lws *wsi, const char **pcce)
221 {
222 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
223 ssize_t plen;
224 int n;
225
226 /* socks proxy */
227 if (!wsi->vhost->socks_proxy_port)
228 return 0;
229
230 if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) {
231 *pcce = "socks msg too large";
232 return -1;
233 }
234 // lwsl_hexdump_notice(pt->serv_buf, plen);
235 n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen,
236 MSG_NOSIGNAL);
237 if (n < 0) {
238 lwsl_debug("ERROR writing socks greeting\n");
239 *pcce = "socks write failed";
240 return -1;
241 }
242
243 lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
244 wsi->context->timeout_secs);
245
246 lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
247
248 return 1;
249 }
250
251 int
lws_socks5c_handle_state(struct lws * wsi,struct lws_pollfd * pollfd,const char ** pcce)252 lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd,
253 const char **pcce)
254 {
255 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
256 int conn_mode = 0, pending_timeout = 0;
257 ssize_t len;
258 int n;
259
260 /* handle proxy hung up on us */
261
262 if (pollfd->revents & LWS_POLLHUP) {
263 lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
264 (void *)wsi, pollfd->fd);
265 *pcce = "socks conn dead";
266 return LW5CHS_RET_BAIL3;
267 }
268
269 n = recv(wsi->desc.sockfd, pt->serv_buf,
270 wsi->context->pt_serv_buf_size, 0);
271 if (n < 0) {
272 if (LWS_ERRNO == LWS_EAGAIN) {
273 lwsl_debug("SOCKS read EAGAIN, retrying\n");
274 return LW5CHS_RET_RET0;
275 }
276 lwsl_err("ERROR reading from SOCKS socket\n");
277 *pcce = "socks recv fail";
278 return LW5CHS_RET_BAIL3;
279 }
280
281 // lwsl_hexdump_warn(pt->serv_buf, n);
282
283 switch (lwsi_state(wsi)) {
284
285 case LRS_WAITING_SOCKS_GREETING_REPLY:
286 if (pt->serv_buf[0] != SOCKS_VERSION_5)
287 goto socks_reply_fail;
288
289 if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
290 lwsl_client("SOCKS GR: No Auth Method\n");
291 if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT,
292 &len)) {
293 lwsl_err("%s: failed to generate connect msg\n",
294 __func__);
295 goto socks_send_msg_fail;
296 }
297 conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
298 pending_timeout =
299 PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
300 goto socks_send;
301 }
302
303 if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
304 lwsl_client("SOCKS GR: User/Pw Method\n");
305 if (lws_socks5c_generate_msg(wsi,
306 SOCKS_MSG_USERNAME_PASSWORD,
307 &len))
308 goto socks_send_msg_fail;
309 conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
310 pending_timeout =
311 PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
312 goto socks_send;
313 }
314 goto socks_reply_fail;
315
316 case LRS_WAITING_SOCKS_AUTH_REPLY:
317 if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
318 pt->serv_buf[1] !=
319 SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
320 goto socks_reply_fail;
321
322 lwsl_client("SOCKS password OK, sending connect\n");
323 if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) {
324 socks_send_msg_fail:
325 *pcce = "socks gen msg fail";
326 return LW5CHS_RET_BAIL3;
327 }
328 conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
329 pending_timeout =
330 PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
331 socks_send:
332 // lwsl_hexdump_notice(pt->serv_buf, len);
333 n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
334 MSG_NOSIGNAL);
335 if (n < 0) {
336 lwsl_debug("ERROR writing to socks proxy\n");
337 *pcce = "socks write fail";
338 return LW5CHS_RET_BAIL3;
339 }
340
341 lws_set_timeout(wsi, pending_timeout,
342 wsi->context->timeout_secs);
343 lwsi_set_state(wsi, conn_mode);
344 break;
345
346 socks_reply_fail:
347 lwsl_err("%s: socks reply: v%d, err %d\n", __func__,
348 pt->serv_buf[0], pt->serv_buf[1]);
349 *pcce = "socks reply fail";
350 return LW5CHS_RET_BAIL3;
351
352 case LRS_WAITING_SOCKS_CONNECT_REPLY:
353 if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
354 pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
355 goto socks_reply_fail;
356
357 lwsl_client("%s: socks connect OK\n", __func__);
358
359 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
360 if (lwsi_role_http(wsi) &&
361 lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
362 wsi->vhost->socks_proxy_address)) {
363 *pcce = "socks connect fail";
364 return LW5CHS_RET_BAIL3;
365 }
366 #endif
367
368 wsi->c_port = wsi->vhost->socks_proxy_port;
369
370 /* clear his proxy connection timeout */
371 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
372 return LW5CHS_RET_STARTHS;
373 default:
374 break;
375 }
376
377 return LW5CHS_RET_NOTHING;
378 }
379