diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c index 4669efd4..75f3a161 100644 --- a/WWW/Library/Implementation/HTTCP.c +++ b/WWW/Library/Implementation/HTTCP.c @@ -1825,6 +1825,10 @@ int HTDoConnect(const char *url, int default_port, int *s) { + /* On error goto jout */ + char *socks5_host; + int socks5_host_len, socks5_port; + const char *socks5_orig_url; int status = 0; char *line = NULL; char *p1 = NULL; @@ -1836,7 +1840,45 @@ int HTDoConnect(const char *url, #else struct sockaddr_in sock_A; struct sockaddr_in *soc_in = &sock_A; +#endif + + /* 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((i = strlen(socks5_host)) > 255){ + HTAlert(gettext("SOCKS5: hostname too long.")); + goto jout; + } + socks5_host_len = (int)i; + + 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); + } +#ifndef INET6 /* * Set up defaults. */ @@ -1859,11 +1901,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 jout; } #else status = HTParseInet(soc_in, host); @@ -1882,16 +1923,12 @@ int HTDoConnect(const char *url, } status = HT_NO_DATA; } - FREE(host); - FREE(line); - return status; + goto jout; } #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 +1937,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 jout; } #else for (res = res0; res; res = res->ai_next) { @@ -1916,7 +1954,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 +2042,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 jout; } set_timeout(&select_timeout); FD_ZERO(&writefds); @@ -2204,7 +2241,6 @@ int HTDoConnect(const char *url, #endif /* !DOSPATH || __DJGPP__ */ #ifdef INET6 - FREE(line); #ifdef NSL_FORK FREE_NSL_FORK(res0); #else @@ -2212,6 +2248,109 @@ 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]; + size_t i; + char const *emsg; + + /* 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){ +jerrsocks: + HTAlert(LYStrerror(errno)); +jesocks: + NETCLOSE(*s); + status = HT_NO_CONNECTION; + goto jout; + } + + /* Receive greeting */ + if(HTDoRead(*s, pbuf, 2) != 2) + goto jerrsocks; + if(pbuf[0] != 0x05 || pbuf[1] != 0x00){ +jesocksreply: + emsg = N_("unexpected reply\n"); +jesocksreplymsg: + HTAlert(gettext(emsg)); + goto jesocks; + } + + /* 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 += sizeof x; + } + if((size_t)write(*s, pbuf, i) != i) + goto jerrsocks; + + /* Connect result */ + if((i = HTDoRead(*s, pbuf, 4)) != 4) + goto jerrsocks; + /* Version 5, reserved must be 0 */ + if(pbuf[0] != 0x05 || pbuf[2] != 0x00) + goto jesocksreply; + /* Result */ + 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) + goto jesocksreplymsg; + + /* 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 jesocksreply; + } + i += sizeof(unsigned short); + if((size_t)HTDoRead(*s, pbuf, i) != i) + goto jerrsocks; + if(i == 1 + sizeof(unsigned short)){ + i = pbuf[0]; + if((size_t)HTDoRead(*s, pbuf, i) != i) + goto jerrsocks; + } + } + +jout: + if(socks5_proxy != NULL){ + FREE(url); + FREE(protocol); + FREE(socks5_host); + } + if(host != NULL); + FREE(host); + if(line != NULL) + FREE(line); return status; } diff --git a/lynx.man b/lynx.man index ff4eb1b8..5a43c71a 100644 --- a/lynx.man +++ b/lynx.man @@ -804,6 +804,11 @@ If enabled the transfer rate is shown in bytes/second. 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. diff --git a/src/LYGlobalDefs.h b/src/LYGlobalDefs.h index e1203c02..577a5948 100644 --- a/src/LYGlobalDefs.h +++ b/src/LYGlobalDefs.h @@ -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 --git a/src/LYMain.c b/src/LYMain.c index 36c22ed5..46b8cee8 100644 --- a/src/LYMain.c +++ b/src/LYMain.c @@ -635,6 +635,8 @@ BOOLEAN debug_display_partial = FALSE; /* Show with MessageSecs delay */ 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 (unrelated to -nosocks!)" + ), PARSE_SET( "soft_dquotes", 4|TOGGLE_ARG, soft_dquotes, "toggles emulation of the old Netscape and Mosaic\n\