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)
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\
_______________________________________________
Lynx-dev mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/lynx-dev

Reply via email to