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