diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f1fd544..c0606f09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ option(LWS_FALLBACK_GETHOSTBYNAME "Also try to do dns resolution using gethostby option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" ON) option(LWS_AVOID_SIGPIPE_IGN "Android 7+ seems to need this" OFF) option(LWS_WITH_STATS "Keep statistics of lws internal operations" OFF) +option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF) if (LWS_WITH_LWSWS) message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV") @@ -1770,6 +1771,7 @@ message(" LWS_WITH_ESP32 = ${LWS_WITH_ESP32}") message(" LWS_WITH_ZIP_FOPS = ${LWS_WITH_ZIP_FOPS}") message(" LWS_AVOID_SIGPIPE_IGN = ${LWS_AVOID_SIGPIPE_IGN}") message(" LWS_WITH_STATS = ${LWS_WITH_STATS}") +message(" LWS_WITH_SOCKS5 = ${LWS_WITH_SOCKS5}") message("---------------------------------------------------------------------") diff --git a/lib/client-handshake.c b/lib/client-handshake.c index c844053d..a9f86650 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -26,6 +26,7 @@ lws_client_connect_2(struct lws *wsi) /* proxy? */ + /* http proxy */ if (wsi->vhost->http_proxy_port) { plen = sprintf((char *)pt->serv_buf, "CONNECT %s:%u HTTP/1.0\x0d\x0a" @@ -49,7 +50,26 @@ lws_client_connect_2(struct lws *wsi) #endif server_addr4.sin_port = htons(wsi->vhost->http_proxy_port); - } else { + } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + else if (wsi->vhost->socks_proxy_port) { + socks_generate_msg(wsi, SOCKS_MSG_GREETING, (size_t *)&plen); + lwsl_client("%s\n", "Sending SOCKS Greeting."); + + ads = wsi->vhost->socks_proxy_address; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + memset(&server_addr6, 0, sizeof(struct sockaddr_in6)); + server_addr6.sin6_port = htons(wsi->vhost->socks_proxy_port); + } else +#endif + server_addr4.sin_port = htons(wsi->vhost->socks_proxy_port); + + } +#endif + else { ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(wsi->vhost)) { @@ -275,6 +295,7 @@ lws_client_connect_2(struct lws *wsi) /* we are connected to server, or proxy */ + /* http proxy */ if (wsi->vhost->http_proxy_port) { /* @@ -303,6 +324,26 @@ lws_client_connect_2(struct lws *wsi) return wsi; } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + else if (wsi->vhost->socks_proxy_port) { + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing greeting to socks proxy" + "socket.\n"); + cce = "socks write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY; + + return wsi; + } +#endif /* * provoke service to issue the handshake directly @@ -754,7 +795,10 @@ lws_client_connect_via_info2(struct lws *wsi) stash->method)) goto bail1; - lws_free_set_NULL(wsi->u.hdr.stash); +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif /* * Check with each extension if it is able to route and proxy this @@ -782,7 +826,10 @@ lws_client_connect_via_info2(struct lws *wsi) return lws_client_connect_2(wsi); bail1: - lws_free_set_NULL(wsi->u.hdr.stash); +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif return NULL; } @@ -836,3 +883,73 @@ lws_client_connect(struct lws_context *context, const char *address, return lws_client_connect_via_info(&i); } +#if defined(LWS_WITH_SOCKS5) +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + size_t *msg_len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + size_t len = 0; + + if (type == SOCKS_MSG_GREETING) { + /* socks version, version 5 only */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* number of methods */ + pt->serv_buf[len++] = 2; + /* username password method */ + pt->serv_buf[len++] = SOCKS_AUTH_USERNAME_PASSWORD; + /* no authentication method */ + pt->serv_buf[len++] = SOCKS_AUTH_NO_AUTH; + } + else if (type == SOCKS_MSG_USERNAME_PASSWORD) { + size_t user_len = 0; + size_t passwd_len = 0; + + user_len = strlen(wsi->vhost->socks_user); + passwd_len = strlen(wsi->vhost->socks_password); + + /* the subnegotiation version */ + pt->serv_buf[len++] = SOCKS_SUBNEGOTIATION_VERSION_1; + /* length of the user name */ + pt->serv_buf[len++] = user_len; + /* user name */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user, + context->pt_serv_buf_size - len); + len += user_len; + /* length of the password */ + pt->serv_buf[len++] = passwd_len; + /* password */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password, + context->pt_serv_buf_size - len); + len += passwd_len; + } + else if (type == SOCKS_MSG_CONNECT) { + size_t len_index = 0; + short net_num = 0; + char *net_buf = (char*)&net_num; + + /* socks version */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* socks command */ + pt->serv_buf[len++] = SOCKS_COMMAND_CONNECT; + /* reserved */ + pt->serv_buf[len++] = 0; + /* address type */ + pt->serv_buf[len++] = SOCKS_ATYP_DOMAINNAME; + len_index = len; + len++; + /* the address we tell SOCKS proxy to connect to */ + strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address, + context->pt_serv_buf_size - len); + len += strlen(wsi->u.hdr.stash->address); + net_num = htons((short)wsi->c_port); + /* the port we tell SOCKS proxy to connect to */ + pt->serv_buf[len++] = net_buf[0]; + pt->serv_buf[len++] = net_buf[1]; + /* the length of the address, excluding port */ + pt->serv_buf[len_index] = strlen(wsi->u.hdr.stash->address); + } + + *msg_len = len; +} +#endif diff --git a/lib/client.c b/lib/client.c index 1919f030..4b643ac1 100755 --- a/lib/client.c +++ b/lib/client.c @@ -81,7 +81,10 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, const char *cce = NULL; unsigned char c; char *sb = p; - int n, len; + int n = 0, len = 0; +#if defined(LWS_WITH_SOCKS5) + char conn_mode = 0, pending_timeout = 0; +#endif switch (wsi->mode) { @@ -101,6 +104,195 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, /* either still pending connection, or changed mode */ return 0; +#if defined(LWS_WITH_SOCKS5) + /* SOCKS Greeting Reply */ + case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read returned EAGAIN..." + "retrying\n"); + return 0; + } + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR reading from SOCKS socket\n"); + return 0; + } + + /* processing greeting reply */ + if (pt->serv_buf[0] == SOCKS_VERSION_5 + && pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) + { + lwsl_client("%s\n", "SOCKS greeting reply received " + "- No Authentication Method"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len); + + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; + lwsl_client("%s\n", "Sending SOCKS connect command"); + } + else if (pt->serv_buf[0] == SOCKS_VERSION_5 + && pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) + { + lwsl_client("%s\n", "SOCKS greeting reply received " + "- User Name Password Method"); + socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, + (size_t *)&len); + + conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; + lwsl_client("%s\n", "Sending SOCKS user/password"); + } + else + { + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR SOCKS greeting reply failed, method " + "code: %d\n", pt->serv_buf[1]); + return 0; + } + + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing socks command to socks proxy " + "socket\n"); + return 0; + } + + lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); + wsi->mode = conn_mode; + + break; + /* SOCKS auth Reply */ + case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read returned EAGAIN... " + "retrying\n"); + return 0; + } + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR reading from socks socket\n"); + return 0; + } + + /* processing auth reply */ + if (pt->serv_buf[0] == SOCKS_SUBNEGOTIATION_VERSION_1 + && pt->serv_buf[1] == SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) + { + lwsl_client("%s\n", "SOCKS password reply recieved - " + "successful"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len); + + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; + lwsl_client("%s\n", "Sending SOCKS connect command"); + } + else + { + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR : SOCKS user/password reply failed, " + "error code: %d\n", pt->serv_buf[1]); + return 0; + } + + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing connect command to SOCKS " + "socket\n"); + return 0; + } + + lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); + wsi->mode = conn_mode; + + break; + + /* SOCKS connect command Reply */ + case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read returned EAGAIN... " + "retrying\n"); + return 0; + } + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR reading from socks socket\n"); + return 0; + } + + /* processing connect reply */ + if (pt->serv_buf[0] == SOCKS_VERSION_5 + && pt->serv_buf[1] == SOCKS_REQUEST_REPLY_SUCCESS) + { + lwsl_client("%s\n", "SOCKS connect reply recieved - " + "successful"); + } + else + { + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR SOCKS connect reply failed, error " + "code: %d\n", pt->serv_buf[1]); + return 0; + } + + /* free stash since we are done with it */ + lws_free_set_NULL(wsi->u.hdr.stash); + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->socks_proxy_address)) + goto bail3; + wsi->c_port = wsi->vhost->socks_proxy_port; + + /* clear his proxy connection timeout */ + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + goto start_ws_hanshake; +#endif case LWSCM_WSCL_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ @@ -149,6 +341,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, * take care of our lws_callback_on_writable * happening at a time when there's no real connection yet */ +#if defined(LWS_WITH_SOCKS5) +start_ws_hanshake: +#endif if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; diff --git a/lib/context.c b/lib/context.c index f791ca6a..e9945c70 100644 --- a/lib/context.c +++ b/lib/context.c @@ -497,9 +497,14 @@ lws_create_vhost(struct lws_context *context, #if !defined(LWS_WITH_ESP8266) vh->http_proxy_port = 0; vh->http_proxy_address[0] = '\0'; +#if defined(LWS_WITH_SOCKS5) + vh->socks_proxy_port = 0; + vh->socks_proxy_address[0] = '\0'; +#endif /* either use proxy from info, or try get it from env var */ + /* http proxy */ if (info->http_proxy_address) { /* override for backwards compatibility */ if (info->http_proxy_port) @@ -512,7 +517,23 @@ lws_create_vhost(struct lws_context *context, lws_set_proxy(vh, p); #endif } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + if (info->socks_proxy_address) { + /* override for backwards compatibility */ + if (info->socks_proxy_port) + vh->socks_proxy_port = info->socks_proxy_port; + lws_set_socks(vh, info->socks_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("socks_proxy"); + if (p) + lws_set_socks(vh, p); #endif + } +#endif +#endif + vh->ka_time = info->ka_time; vh->ka_interval = info->ka_interval; vh->ka_probes = info->ka_probes; diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 9fd89cd9..b8e89745 100755 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1325,6 +1325,79 @@ auth_too_long: return -1; } +#if defined(LWS_WITH_SOCKS5) +LWS_VISIBLE int +lws_set_socks(struct lws_vhost *vhost, const char *socks) +{ +#if !defined(LWS_WITH_ESP8266) + char *p_at, *p_colon; + char user[96]; + char password[96]; + + if (!socks) + return -1; + + vhost->socks_user[0] = '\0'; + vhost->socks_password[0] = '\0'; + + p_at = strchr(socks, '@'); + if (p_at) { /* auth is around */ + if ((unsigned int)(p_at - socks) > (sizeof(user) + + sizeof(password) - 2)) { + lwsl_err("Socks auth too long\n"); + goto bail; + } + + p_colon = strchr(socks, ':'); + if (p_colon) { + if ((unsigned int)(p_colon - socks) > (sizeof(user) + - 1) ) { + lwsl_err("Socks user too long\n"); + goto bail; + } + if ((unsigned int)(p_at - p_colon) > (sizeof(password) + - 1) ) { + lwsl_err("Socks password too long\n"); + goto bail; + } + } + strncpy(vhost->socks_user, socks, p_colon - socks); + strncpy(vhost->socks_password, p_colon + 1, + p_at - (p_colon + 1)); + + lwsl_info(" Socks auth, user: %s, password: %s\n", + vhost->socks_user, vhost->socks_password ); + + socks = p_at + 1; + } + + strncpy(vhost->socks_proxy_address, socks, + sizeof(vhost->socks_proxy_address) - 1); + vhost->socks_proxy_address[sizeof(vhost->socks_proxy_address) - 1] + = '\0'; + + p_colon = strchr(vhost->socks_proxy_address, ':'); + if (!p_colon && !vhost->socks_proxy_port) { + lwsl_err("socks_proxy needs to be address:port\n"); + return -1; + } else { + if (p_colon) { + *p_colon = '\0'; + vhost->socks_proxy_port = atoi(p_colon + 1); + } + } + + lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address, + vhost->socks_proxy_port); + + return 0; + +bail: +#endif + return -1; +} +#endif + LWS_VISIBLE const struct lws_protocols * lws_get_protocol(struct lws *wsi) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 4eb0cd05..185a5690 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1812,7 +1812,7 @@ struct lws_context_creation_info { /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ void *user; /**< CONTEXT: optional user pointer that can be recovered via the context - * pointer using lws_context_user */ + * pointer using lws_context_user */ int ka_time; /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive * timeout to all libwebsocket sockets, client or server */ @@ -1955,6 +1955,11 @@ struct lws_context_creation_info { */ int simultaneous_ssl_restriction; /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions possible.*/ + const char *socks_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int socks_proxy_port; + /**< VHOST: If socks_proxy_address was non-NULL, uses this port */ /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility @@ -2070,6 +2075,25 @@ lws_context_is_deprecated(struct lws_context *context); LWS_VISIBLE LWS_EXTERN int lws_set_proxy(struct lws_vhost *vhost, const char *proxy); +/** + * lws_set_socks() - Setup socks to lws_context. + * \param vhost: pointer to struct lws_vhost you want set socks for + * \param socks: pointer to c string containing socks in format address:port + * + * Returns 0 if socks string was parsed and socks was setup. + * Returns -1 if socks is NULL or has incorrect format. + * + * This is only required if your OS does not provide the socks_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_socks(struct lws_vhost *vhost, const char *socks); struct lws_vhost; @@ -3471,6 +3495,9 @@ enum pending_timeout { PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, + PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, + PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, /****** add new things just above ---^ ******/ }; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 8de93dc0..ad61015f 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -574,12 +574,75 @@ enum connection_mode { LWSCM_WSCL_WAITING_SERVER_REPLY, LWSCM_WSCL_WAITING_EXTENSION_CONNECT, LWSCM_WSCL_PENDING_CANDIDATE_CHILD, + LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY, + LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY, + LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY, /****** add new things just above ---^ ******/ }; +/* enums of socks version */ +enum socks_version { + SOCKS_VERSION_4 = 4, + SOCKS_VERSION_5 = 5 +}; + +/* enums of subnegotiation version */ +enum socks_subnegotiation_version { + SOCKS_SUBNEGOTIATION_VERSION_1 = 1, +}; + +/* enums of socks commands */ +enum socks_command { + SOCKS_COMMAND_CONNECT = 1, + SOCKS_COMMAND_BIND = 2, + SOCKS_COMMAND_UDP_ASSOCIATE = 3 +}; + +/* enums of socks address type */ +enum socks_atyp { + SOCKS_ATYP_IPV4 = 1, + SOCKS_ATYP_DOMAINNAME = 3, + SOCKS_ATYP_IPV6 = 4 +}; + +/* enums of socks authentication methods */ +enum socks_auth_method { + SOCKS_AUTH_NO_AUTH = 0, + SOCKS_AUTH_GSSAPI = 1, + SOCKS_AUTH_USERNAME_PASSWORD = 2 +}; + +/* enums of subnegotiation status */ +enum socks_subnegotiation_status { + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, +}; + +/* enums of socks request reply */ +enum socks_request_reply { + SOCKS_REQUEST_REPLY_SUCCESS = 0, + SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, + SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, + SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, + SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, + SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, + SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, + SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, + SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 +}; + +/* enums used to generate socks messages */ +enum socks_msg_type { + /* greeting */ + SOCKS_MSG_GREETING, + /* credential, user name and password */ + SOCKS_MSG_USERNAME_PASSWORD, + /* connect command */ + SOCKS_MSG_CONNECT +}; + enum { LWS_RXFLOW_ALLOW = (1 << 0), LWS_RXFLOW_PENDING_CHANGE = (1 << 1), @@ -774,6 +837,11 @@ struct lws_vhost { #if !defined(LWS_WITH_ESP8266) char http_proxy_address[128]; char proxy_basic_auth_token[128]; +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif #endif #if defined(LWS_WITH_ESP8266) /* listen sockets need a place to hang their hat */ @@ -801,6 +869,9 @@ struct lws_vhost { int listen_port; unsigned int http_proxy_port; +#if defined(LWS_WITH_SOCKS5) + unsigned int socks_proxy_port; +#endif unsigned int options; int count_protocols; int ka_time; @@ -2103,6 +2174,10 @@ static inline uint64_t lws_stats_atomic_max(struct lws_context * context, (void)context; (void)pt; (void)index; (void)val; return 0; } #endif +/* socks */ +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + size_t *msg_len); + #ifdef __cplusplus }; #endif diff --git a/lws_config.h.in b/lws_config.h.in index 96c17053..c9a0a1ff 100644 --- a/lws_config.h.in +++ b/lws_config.h.in @@ -134,6 +134,7 @@ #cmakedefine LWS_FALLBACK_GETHOSTBYNAME #cmakedefine LWS_WITH_STATS +#cmakedefine LWS_WITH_SOCKS5 /* OpenSSL various APIs */