# patch by Thomas E. Dickey # created Mon Sep 16 00:29:40 UTC 2019 # ------------------------------------------------------------------------------ # WWW/Library/Implementation/HTTCP.c | 215 ++++++++++++++++++++++++++++++++--- # lynx.man | 7 - # src/LYGlobalDefs.h | 3 # src/LYMain.c | 8 + # 4 files changed, 213 insertions(+), 20 deletions(-) # ------------------------------------------------------------------------------ Index: WWW/Library/Implementation/HTTCP.c --- lynx2.9.0dev.4c+/WWW/Library/Implementation/HTTCP.c 2018-12-26 13:16:59.000000000 +0000 +++ lynx2.9.0dev.4d/WWW/Library/Implementation/HTTCP.c 2019-09-16 00:27:19.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $LynxId: HTTCP.c,v 1.151 2018/12/26 13:16:59 tom Exp $ + * $LynxId: HTTCP.c,v 1.151.1.13 2019/09/16 00:27:19 tom Exp tom $ * * Generic Communication Code HTTCP.c * ========================== @@ -1825,10 +1825,17 @@ int default_port, int *s) { - int status = 0; + char *socks5_host; + unsigned socks5_host_len = 0; + int socks5_port; + const char *socks5_orig_url; + char *socks5_new_url = NULL; + char *socks5_protocol = NULL; + 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 +1843,49 @@ #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) { + int xport; + + xport = default_port; + socks5_orig_url = url; + StrAllocCopy(socks5_new_url, url); + + /* Get node name and optional port number of wanted URL */ + p1 = HTParse(socks5_new_url, "", PARSE_HOST); + socks5_host = NULL; + StrAllocCopy(socks5_host, p1); + strip_userid(socks5_host, FALSE); + FREE(p1); + + if (strlen(socks5_host) > 255) { + emsg = gettext("SOCKS5: hostname too long."); + status = HT_ERROR; + goto report_error; + } + socks5_host_len = (unsigned) strlen(socks5_host); + + if (HTParsePort(socks5_new_url, &socks5_port) == NULL) + socks5_port = xport; + FREE(socks5_new_url); + + /* And switch over to our SOCKS5 config; in order to embed that into + * lynx environment, prepend protocol prefix */ + default_port = 1080; /* RFC 1928 */ + HTSACat(&socks5_new_url, "socks://"); + HTSACat(&socks5_new_url, socks5_proxy); + url = socks5_new_url; + + socks5_protocol = HTSprintf0(NULL, + gettext("(for %s at %s) SOCKS5"), + protocol, socks5_host); + protocol = socks5_protocol; + } +#ifndef INET6 /* * Set up defaults. */ @@ -1861,9 +1910,8 @@ if (res0 == NULL) { HTSprintf0(&line, gettext("Unable to locate remote host %s."), host); _HTProgress(line); - FREE(host); - FREE(line); - return HT_NO_DATA; + status = HT_NO_DATA; + goto cleanup; } #else status = HTParseInet(soc_in, host); @@ -1882,16 +1930,12 @@ } 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. @@ -1899,8 +1943,9 @@ #ifndef INET6 *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (*s == -1) { - HTAlert(gettext("socket failed.")); - return HT_NO_DATA; + status = HT_NO_DATA; + emsg = gettext("socket failed."); + goto report_error; } #else for (res = res0; res; res = res->ai_next) { @@ -1916,7 +1961,6 @@ gettext("socket failed: family %d addr %s port %s."), res->ai_family, hostbuf, portbuf); _HTProgress(line); - FREE(line); continue; } #endif /* INET6 */ @@ -2005,13 +2049,13 @@ 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 +2248,6 @@ #endif /* !DOSPATH || __DJGPP__ */ #ifdef INET6 - FREE(line); #ifdef NSL_FORK FREE_NSL_FORK(res0); #else @@ -2212,11 +2255,149 @@ 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; + } else if (HTDoRead(*s, pbuf, 2) != 2) { + goto report_system_err; + } else 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; + /* C99 */ { + unsigned short x; /* XXX 16-bit? */ + + x = htons(socks5_port); + memcpy(&pbuf[i], (unsigned char *) &x, sizeof x); + i += (unsigned) sizeof(x); + } + if ((size_t) write(*s, pbuf, i) != i) { + goto report_system_err; + } else if ((i = (unsigned) HTDoRead(*s, pbuf, 4)) != 4) { + goto report_system_err; + } + /* Version 5, reserved must be 0 */ + if (pbuf[0] == 0x05 && pbuf[2] == 0x00) { + /* Result */ + switch (pbuf[1]) { + case 0x00: + emsg = NULL; + break; + case 0x01: + emsg = gettext("SOCKS server failure"); + break; + case 0x02: + emsg = gettext("connection not allowed by ruleset"); + break; + case 0x03: + emsg = gettext("network unreachable"); + break; + case 0x04: + emsg = gettext("host unreachable"); + break; + case 0x05: + emsg = gettext("connection refused"); + break; + case 0x06: + emsg = gettext("TTL expired"); + break; + case 0x07: + emsg = gettext("command not supported"); + break; + case 0x08: + emsg = gettext("address type not supported"); + break; + default: + emsg = gettext("unknown SOCKS error code"); + break; + } + if (emsg != NULL) { + goto report_no_connection; + } + } else { + goto report_unexpected_reply; + } + + /* 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 += (unsigned) sizeof(unsigned short); + + if ((size_t) HTDoRead(*s, pbuf, i) != i) { + goto report_system_err; + } else if (i == 1 + sizeof(unsigned short)) { + i = pbuf[0]; + if ((size_t) HTDoRead(*s, pbuf, i) != i) { + goto report_system_err; + } + } + } + goto cleanup; + + report_system_err: + emsg = LYStrerror(errno); + goto report_no_connection; + + report_unexpected_reply: + emsg = gettext("unexpected reply\n"); + /* FALLTHRU */ + + report_no_connection: + status = HT_NO_CONNECTION; + /* FALLTHRU */ + + report_error: + HTAlert(emsg); + if (*s != -1) { + NETCLOSE(*s); + } + + cleanup: + if (socks5_proxy != NULL) { + FREE(socks5_new_url); + FREE(socks5_protocol); + FREE(socks5_host); + } + FREE(host); + FREE(line); return status; } /* - * This is so interruptible reads can be implemented cleanly. + * This is interruptible so reads can be implemented cleanly. */ int HTDoRead(int fildes, void *buf, Index: lynx.man --- lynx2.9.0dev.4c+/lynx.man 2019-08-17 01:07:24.000000000 +0000 +++ lynx2.9.0dev.4d/lynx.man 2019-09-15 22:20:44.000000000 +0000 @@ -1,4 +1,4 @@ -.\" $LynxId: lynx.man,v 1.124 2019/08/17 01:07:24 tom Exp $ +.\" $LynxId: lynx.man,v 1.124.1.1 2019/09/15 22:20:44 Steffen.Nurpmeso Exp tom $ .\" ************************************************************************** .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds AQ \(aq @@ -804,6 +804,11 @@ 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. +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. Index: src/LYGlobalDefs.h --- lynx2.9.0dev.4c+/src/LYGlobalDefs.h 2019-01-25 02:00:58.000000000 +0000 +++ lynx2.9.0dev.4d/src/LYGlobalDefs.h 2019-09-15 22:20:44.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $LynxId: LYGlobalDefs.h,v 1.150 2019/01/25 02:00:58 tom Exp $ + * $LynxId: LYGlobalDefs.h,v 1.150.1.1 2019/09/15 22:20:44 Steffen.Nurpmeso Exp tom $ * * global variable definitions */ @@ -441,6 +441,7 @@ 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 */ Index: src/LYMain.c --- lynx2.9.0dev.4c+/src/LYMain.c 2019-01-25 00:06:42.000000000 +0000 +++ lynx2.9.0dev.4d/src/LYMain.c 2019-09-15 22:20:44.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $LynxId: LYMain.c,v 1.285 2019/01/25 00:06:42 tom Exp $ + * $LynxId: LYMain.c,v 1.285.1.1 2019/09/15 22:20:44 Steffen.Nurpmeso Exp tom $ */ #include #include @@ -636,6 +636,8 @@ 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; @@ -3907,6 +3909,10 @@ "toggles display of transfer rate" ), #endif + PARSE_STR( + "socks5-proxy", 2|NEED_LYSTRING_ARG, socks5_proxy, + "=URL\n(via which) SOCKS5 proxy to connect (unrelated to -nosocks!)" + ), PARSE_SET( "soft_dquotes", 4|TOGGLE_ARG, soft_dquotes, "toggles emulation of the old Netscape and Mosaic\n\