about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-10-13 15:06:20 +0200
committerbptato <nincsnevem662@gmail.com>2024-10-13 15:06:20 +0200
commitb773f053478dfae83719417b434f9e232d374b0b (patch)
treedb7e449c42c734df581a00a7dbdf41c9e86f3946
parenta252f9695d8c95d2eb1aaf7d25e720ac84765be6 (diff)
downloadchawan-b773f053478dfae83719417b434f9e232d374b0b.tar.gz
lcgi: support HTTP proxy
not quite sure this approach is right, because it still won't work with
HTTPS without linking everything with OpenSSL...

maybe a socks5 bridge through a UNIX socket would work better?
-rw-r--r--Makefile1
-rw-r--r--adapter/protocol/lcgi.nim67
2 files changed, 53 insertions, 15 deletions
diff --git a/Makefile b/Makefile
index 587d0ba6..52c75e55 100644
--- a/Makefile
+++ b/Makefile
@@ -113,6 +113,7 @@ $(OUTDIR_CGI_BIN)/canvas: src/types/canvastypes.nim src/types/path.nim \
 $(OUTDIR_CGI_BIN)/resize: adapter/img/stb_image_resize.h adapter/img/stb_image_resize.c \
 	src/utils/sandbox.nim $(dynstream) $(twtstr)
 $(OUTDIR_LIBEXEC)/urlenc: $(twtstr)
+$(OUTDIR_LIBEXEC)/nc: $(lcgi)
 $(OUTDIR_LIBEXEC)/gopher2html: adapter/gophertypes.nim $(twtstr)
 $(OUTDIR_LIBEXEC)/ansi2html: src/types/color.nim src/io/poll.nim $(twtstr) $(dynstream)
 $(OUTDIR_LIBEXEC)/md2html: $(twtstr)
diff --git a/adapter/protocol/lcgi.nim b/adapter/protocol/lcgi.nim
index 89932ba9..590c63c8 100644
--- a/adapter/protocol/lcgi.nim
+++ b/adapter/protocol/lcgi.nim
@@ -73,7 +73,8 @@ proc authenticateSocks5(os, ps: PosixStream; buf: array[2, uint8];
   else:
     os.die("ProxyInvalidResponse received wrong auth method " & $buf[1])
 
-proc sendSocks5Domain(os, ps: PosixStream; host, port: string) =
+proc sendSocks5Domain(os, ps: PosixStream; host, port: string;
+    outIpv6: var bool) =
   if host.len > 255:
     os.die("InternalError", "host too long to send to proxy")
   let dstaddr = "\x03" & char(host.len) & host
@@ -93,21 +94,25 @@ proc sendSocks5Domain(os, ps: PosixStream; host, port: string) =
   of 0x01:
     var ipv4 = default(array[4, uint8])
     ps.recvDataLoop(ipv4)
+    outIpv6 = false
   of 0x03:
     var len = [0u8]
     ps.recvDataLoop(len)
     var domain = newString(int(len[0]))
     ps.recvDataLoop(domain)
+    # we don't really know, so just assume it's ipv4.
+    outIpv6 = false
   of 0x04:
     var ipv6 = default(array[16, uint8])
     ps.recvDataLoop(ipv6)
+    outIpv6 = true
   else:
     os.die("ProxyInvalidResponse")
   var bndport = default(array[2, uint8])
   ps.recvDataLoop(bndport)
 
 proc connectSocks5Socket(os: PosixStream; host, port, proxyHost, proxyPort,
-    proxyUser, proxyPass: string): PosixStream =
+    proxyUser, proxyPass: string; outIpv6: var bool): PosixStream =
   var dummy = false
   let ps = os.connectSocket(proxyHost, proxyPort, "FailedToResolveProxy",
     "ProxyRefusedToConnect", dummy)
@@ -117,15 +122,42 @@ proc connectSocks5Socket(os: PosixStream; host, port, proxyHost, proxyPort,
   var buf = default(array[2, uint8])
   ps.recvDataLoop(buf)
   os.authenticateSocks5(ps, buf, proxyUser, proxyPass)
-  os.sendSocks5Domain(ps, host, port)
+  os.sendSocks5Domain(ps, host, port, outIpv6)
+  return ps
+
+proc connectHTTPSocket(os: PosixStream; host, port, proxyHost, proxyPort,
+    proxyUser, proxyPass: string): PosixStream =
+  var dummy = false
+  let ps = os.connectSocket(proxyHost, proxyPort, "FailedToResolveProxy",
+    "ProxyRefusedToConnect", dummy)
+  var buf = "CONNECT " & host & ':' & port & " HTTP/1.1\r\n"
+  buf &= "Host: " & host & ':' & port & "\r\n"
+  if proxyUser != "" or proxyPass != "":
+    let s = btoa(proxyUser & ' ' & proxyPass)
+    buf &= "Proxy-Authorization: basic " & s & "\r\n"
+  buf &= "\r\n"
+  ps.sendDataLoop(buf)
+  var res = ""
+  var crlfState = 0
+  while crlfState < 4:
+    var buf = [char(0)]
+    let n = ps.recvData(buf)
+    if n == 0:
+      break
+    let expected = ['\r', '\n'][crlfState mod 2]
+    if buf[0] == expected:
+      inc crlfState
+    else:
+      crlfState = 0
+    res &= buf[0]
+  if not res.startsWithIgnoreCase("HTTP/1.1 200") and
+      not res.startsWithIgnoreCase("HTTP/1.0 200"):
+    os.die("ProxyRefusedToConnect")
   return ps
 
 proc connectProxySocket(os: PosixStream; host, port, proxy: string;
     outIpv6: var bool): PosixStream =
   let scheme = proxy.until(':')
-  # We always use socks5h, actually.
-  if scheme != "socks5" and scheme != "socks5h":
-    os.die("InternalError", "only socks5 proxy is supported")
   var i = scheme.len + 1
   while i < proxy.len and proxy[i] == '/':
     inc i
@@ -138,16 +170,21 @@ proc connectProxySocket(os: PosixStream; host, port, proxy: string;
     pass = auth.after(':')
     i = authi + 1
   var proxyHost = ""
-  while i < proxy.len:
-    let c = proxy[i]
-    if c == ':':
-      inc i
-      break
-    if c != '/':
-      proxyHost &= c
+  while i < proxy.len and proxy[i] notin {':', '/'}:
+    proxyHost &= proxy[i]
+    inc i
+  inc i
+  var proxyPort = ""
+  while i < proxy.len and proxy[i] in AsciiDigit:
+    proxyPort &= proxy[i]
     inc i
-  let proxyPort = proxy.substr(i)
-  return os.connectSocks5Socket(host, port, proxyHost, proxyPort, user, pass)
+  if scheme == "socks5" or scheme == "socks5h":
+    # We always use socks5h, actually.
+    return os.connectSocks5Socket(host, port, proxyHost, proxyPort, user, pass,
+      outIpv6)
+  elif scheme == "http":
+    return os.connectHTTPSocket(host, port, proxyHost, proxyPort, user, pass)
+  os.die("InternalError", "only socks5 or http proxies are supported")
 
 # Note: outIpv6 is not read; it just indicates whether the socket's
 # address is IPv6.