diff -Napru -u lynx2.8.9rel.1.orig/lynx.man lynx2.8.9rel.1/lynx.man --- lynx2.8.9rel.1.orig/lynx.man 2018-07-08 12:54:20.000000000 +0200 +++ lynx2.8.9rel.1/lynx.man 2019-09-16 22:27:31.540865464 +0200 @@ -804,6 +804,11 @@ If enabled the transfer rate is shown in If disabled, no transfer rate is shown. Use lynx.cfg or the options menu to select KB/second and/or ETA. .TP +.B \-socks5-proxy=URL +(Via which) SOCKS5 proxy to connect to. +This controls the builtin SOCKS5 support, and is therefore unrelated to +the option \fB\-nosocks\fP. +.TP .B \-soft_dquotes toggles emulation of the old Netscape and Mosaic bug which treated \*(``>\*('' as a co-terminator for double-quotes and tags. diff -Napru -u lynx2.8.9rel.1.orig/src/LYGlobalDefs.h lynx2.8.9rel.1/src/LYGlobalDefs.h --- lynx2.8.9rel.1.orig/src/LYGlobalDefs.h 2018-03-28 01:05:13.000000000 +0200 +++ lynx2.8.9rel.1/src/LYGlobalDefs.h 2019-09-16 22:19:08.160874980 +0200 @@ -441,6 +441,7 @@ extern "C" { extern BOOLEAN debug_display_partial; /* show with MessageSecs delay */ extern BOOLEAN display_partial_flag; /* permanent flag, not mutable */ #endif + extern char *socks5_proxy; extern char *form_post_data; /* User data for post form */ extern char *form_get_data; /* User data for get form */ extern char *http_error_file; /* Place HTTP status code in this file */ diff -Napru -u lynx2.8.9rel.1.orig/src/LYMain.c lynx2.8.9rel.1/src/LYMain.c --- lynx2.8.9rel.1.orig/src/LYMain.c 2018-07-08 17:22:44.000000000 +0200 +++ lynx2.8.9rel.1/src/LYMain.c 2019-09-16 22:27:23.827532277 +0200 @@ -635,6 +635,8 @@ BOOLEAN debug_display_partial = FALSE; / int partial_threshold = -1; /* # of lines to be d/l'ed until we repaint */ #endif +char *socks5_proxy = NULL; + BOOLEAN LYNonRestartingSIGWINCH = FALSE; BOOLEAN LYReuseTempfiles = FALSE; BOOLEAN LYUseBuiltinSuffixes = TRUE; @@ -3910,6 +3912,10 @@ saves session to that file on exit" "toggles display of transfer rate" ), #endif + PARSE_STR( + "socks5-proxy", 2|NEED_LYSTRING_ARG, socks5_proxy, + "=URL\n(Via which) SOCKS5 proxy to connect to (unrelated to -nosocks!)" + ), PARSE_SET( "soft_dquotes", 4|TOGGLE_ARG, soft_dquotes, "toggles emulation of the old Netscape and Mosaic\n\ diff -Napru -u lynx2.8.9rel.1.orig/WWW/Library/Implementation/HTTCP.c lynx2.8.9rel.1/WWW/Library/Implementation/HTTCP.c --- lynx2.8.9rel.1.orig/WWW/Library/Implementation/HTTCP.c 2018-05-16 21:48:49.000000000 +0200 +++ lynx2.8.9rel.1/WWW/Library/Implementation/HTTCP.c 2019-09-16 22:53:29.772696773 +0200 @@ -1825,10 +1825,16 @@ int HTDoConnect(const char *url, int default_port, int *s) { - int status = 0; + /* On error goto cleanup */ + char *socks5_host; + unsigned socks5_host_len; + int socks5_port; + const char *socks5_orig_url; + int status = HT_OK; char *line = NULL; char *p1 = NULL; char *host = NULL; + char const *emsg; #ifdef INET6 LYNX_ADDRINFO *res = 0, *res0 = 0; @@ -1836,7 +1842,48 @@ int HTDoConnect(const char *url, #else struct sockaddr_in sock_A; struct sockaddr_in *soc_in = &sock_A; +#endif + + *s = -1; /* nothing is open yet */ + + /* In case of a present SOCKS5 proxy, marshal */ + if ((socks5_orig_url = socks5_proxy) != NULL) { + size_t i; + int xport; + + xport = default_port; + socks5_orig_url = url; + + /* Get node name and optional port number of wanted URL */ + p1 = HTParse(url, "", PARSE_HOST); + socks5_host = NULL; + StrAllocCopy(socks5_host, p1); + strip_userid(socks5_host, FALSE); + FREE(p1); + + if (HTParsePort((char *) url, &socks5_port) == NULL) + socks5_port = xport; + + /* And switch over to our SOCKS5 config; in order to embed that into + * lynx environment, prepend protocol prefix */ + default_port = 1080; /* RFC 1928 */ + HTSACat(&p1, "socks://"); + HTSACat(&p1, socks5_proxy); + url = p1; + p1 = NULL; + + protocol = HTSprintf0(NULL, gettext("(for %s at %s) SOCKS5"), + protocol, socks5_host); + + if ((i = strlen(socks5_host)) > 255) { + HTAlert(gettext("SOCKS5: hostname too long.")); + status = HT_ERROR; + goto cleanup; + } + socks5_host_len = (int) i; + } +#ifndef INET6 /* * Set up defaults. */ @@ -1859,11 +1906,10 @@ int HTDoConnect(const char *url, /* HTParseInet() is useless! */ res0 = HTGetAddrInfo(host, default_port); if (res0 == NULL) { - HTSprintf0(&line, gettext("Unable to locate remote host %s."), host); - _HTProgress(line); - FREE(host); - FREE(line); - return HT_NO_DATA; + HTSprintf0(&line, gettext("Unable to locate remote host %s."), host); + _HTProgress(line); + status = HT_NO_DATA; + goto cleanup; } #else status = HTParseInet(soc_in, host); @@ -1882,16 +1928,12 @@ int HTDoConnect(const char *url, } status = HT_NO_DATA; } - FREE(host); - FREE(line); - return status; + goto cleanup; } #endif /* INET6 */ HTSprintf0(&line, gettext("Making %s connection to %s"), protocol, host); _HTProgress(line); - FREE(host); - FREE(line); /* * Now, let's get a socket set up from the server for the data. @@ -1900,7 +1942,8 @@ int HTDoConnect(const char *url, *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (*s == -1) { HTAlert(gettext("socket failed.")); - return HT_NO_DATA; + status = HT_NO_DATA; + goto cleanup; } #else for (res = res0; res; res = res->ai_next) { @@ -1916,7 +1959,6 @@ int HTDoConnect(const char *url, gettext("socket failed: family %d addr %s port %s."), res->ai_family, hostbuf, portbuf); _HTProgress(line); - FREE(line); continue; } #endif /* INET6 */ @@ -2005,13 +2047,13 @@ int HTDoConnect(const char *url, if ((tries++ / TRIES_PER_SECOND) >= connect_timeout) { HTAlert(gettext("Connection failed (too many retries).")); #ifdef INET6 - FREE(line); #ifndef NSL_FORK if (res0) freeaddrinfo(res0); #endif #endif /* INET6 */ - return HT_NO_DATA; + status = HT_NO_DATA; + goto cleanup; } set_timeout(&select_timeout); FD_ZERO(&writefds); @@ -2204,7 +2246,6 @@ int HTDoConnect(const char *url, #endif /* !DOSPATH || __DJGPP__ */ #ifdef INET6 - FREE(line); #ifdef NSL_FORK FREE_NSL_FORK(res0); #else @@ -2212,7 +2253,143 @@ int HTDoConnect(const char *url, freeaddrinfo(res0); #endif #endif /* INET6 */ + + /* Now if this was a SOCKS5 proxy connection, go for the real one */ + if (status >= 0 && socks5_orig_url != NULL) { + unsigned char pbuf[4 + 1 + 255 + 2]; + unsigned i; + + /* RFC 1928: version identifier/method selection message */ + pbuf[0] = 0x05; /* VER: protocol version: X'05' */ + pbuf[1] = 0x01; /* NMETHODS: 1 */ + pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */ + if (write(*s, pbuf, 3) != 3) + goto report_system_err; + + /* Receive greeting */ + if (HTDoRead(*s, pbuf, 2) != 2) + goto report_system_err; + + if (pbuf[0] != 0x05 || pbuf[1] != 0x00) + goto report_unexpected_reply; + + /* RFC 1928: CONNECT request */ + HTSprintf0(&line, gettext("SOCKS5: connecting to %s"), socks5_host); + _HTProgress(line); + + pbuf[0] = 0x05; /* VER: protocol version: X'05' */ + pbuf[1] = 0x01; /* CMD: CONNECT X'01' */ + pbuf[2] = 0x00; /* RESERVED */ + pbuf[3] = 0x03; /* ATYP: domain name */ + pbuf[4] = (unsigned char)socks5_host_len; + memcpy(&pbuf[i = 5], socks5_host, socks5_host_len); + i += socks5_host_len; + pbuf[i++] = ((unsigned)socks5_port >> 8) & 0xFF; + pbuf[i++] = (unsigned)socks5_port & 0xFF; + if ((size_t) write(*s, pbuf, i) != i) + goto report_system_err; + + /* Connect result */ + if (HTDoRead(*s, pbuf, 4) != 4) + goto report_system_err; + + /* Version 5, reserved must be 0 */ + if (pbuf[0] != 0x05 || pbuf[2] != 0x00) + goto report_unexpected_reply; + switch (pbuf[1]) { + case 0x00: + emsg = NULL; + break; + case 0x01: + emsg = N_("SOCKS server failure"); + break; + case 0x02: + emsg = N_("connection not allowed by ruleset"); + break; + case 0x03: + emsg = N_("network unreachable"); + break; + case 0x04: + emsg = N_("host unreachable"); + break; + case 0x05: + emsg = N_("connection refused"); + break; + case 0x06: + emsg = N_("TTL expired"); + break; + case 0x07: + emsg = N_("command not supported"); + break; + case 0x08: + emsg = N_("address type not supported"); + break; + default: + emsg = N_("unknown SOCKS error code"); + break; + } + if (emsg != NULL) { + emsg = gettext(emsg); + goto report_no_connection; + } + + /* Address type variable; read the BND.PORT with it. + * This is actually false since RFC 1928 says that the BND.ADDR reply + * to CONNECT contains the IP address, so only 0x01 and 0x04 are + * allowed */ + switch (pbuf[3]) { + case 0x01: + i = 4; + break; + case 0x03: + i = 1; + break; + case 0x04: + i = 16; + break; + default: + goto report_unexpected_reply; + } + i += 2; + if ((unsigned) HTDoRead(*s, pbuf, i) != i) + goto report_system_err; + if (i == 1 + 2) { + i = pbuf[0]; + if ((unsigned) HTDoRead(*s, pbuf, i) != i) + goto report_system_err; + } + } + + cleanup: + if (socks5_proxy != NULL) { + FREE(url); + FREE(protocol); + FREE(socks5_host); + } + if (host != NULL); + FREE(host); + if (line != NULL) + FREE(line); return status; + + report_system_err: + emsg = LYStrerror(errno); + goto report_no_connection; + + report_unexpected_reply: + emsg = gettext("unexpected reply"); + /* FALLTHRU */ + + report_no_connection: + status = HT_NO_CONNECTION; + /* FALLTHRU */ + + report_error: + HTAlert(emsg); + if (*s != -1) + NETCLOSE(*s); + + goto cleanup; } /*