Re: ..on SOCKS5 support

Steffen Nurpmeso <[email protected]>
Newsgroups gmane.comp.web.lynx.devel
Message-ID <20190914220418.j7nb7%[email protected]>
Oh, hello and good evening, for auld lang syne!

Steffen Nurpmeso wrote in <20190911141510.7s2dR%[email protected]>:
 |please excuse the late reply.

Terrible!  I apologise for the silence.

  ...

Thomas Dickey wrote in <20190907195941.c2gqjhnuj2pp44az@prl-debianold-64\
.jexium-island.net>:
 |On Sat, Sep 07, 2019 at 08:51:26PM +0200, Steffen Nurpmeso wrote:
 |> I was forced to use lynx via SOCKS5 proxy, which i never did
 |> before.  To my surprise that did not work, it needs an additional
 |> library.  That increased the surprise, because all one needs for
 |> SOCKS5 support, even including the DNS lookup, is to hook
 |> connect(2) -- that is how i did it, as easy as [1].  And in fact
 |
 |That's changed over time - there's no standard/portable implementation
 |of socks5, and (the last time I looked...) none of the extent versions
 |corresponded to what lynx worked with long ago :-)
 |
 |> i became inspired by Gaetan Bisson's usocks-06.c, which can be
 |> used via $LD_PRELOAD, and which got me going.  I will attach it.
 |>
 |> Wouldn't it be much nicer if there would be a -socks=[HOST]:PORT
 |> command line argument and a simple, always compiled in, wrapper
 |> around connect, just the way i do it (in [1])?
 |
 |that's a thought...

 ...

Thomas Dickey wrote in <20190910092328.5aevtmukues22322@prl-debianold-64\
.jexium-island.net>:
 |On Mon, Sep 09, 2019 at 03:43:59PM +0200, Steffen Nurpmeso wrote:
 |> Thomas Dickey wrote in <20190908105750.qc3ut2e3rkrswfm4@prl-debianold-64\
 |> .jexium-island.net>:
 |...
 |>|If you have something that works reasonably well on one of the BSDs,
 |>|making it more portable (adding configure checks, command-line options,
 |>|etc.) is fairly routine.
 |>
 |> That is BSD socket code + read(2) + write(2).
 |> I will throw an eye as soon as possible, and report back then, ok?
 |
 |sounds good

Right.  So here is a working patch, porting over my own SOCKS5
code with slight adjustments for lynx.  It took hours to realize
that PARSE_HOST requires a protocol prefix, _what_a_mess_.  Note
the diff was finally produced on v2-8-9rel_1, i tried several tags
from v2-9-0dev_4a downwards, but could not get something
compilable.  (But git cherry-pick'ing the patch worked.)
I hope you like it, it works fine for me here?

Ciao, and a nice Sunday i wish from Germany!

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)

_______________________________________________
Lynx-dev mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/lynx-dev
lynx-socks5.diff (text/x-diff, 9.4 KB)
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\
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.