osbase/socksproxy: implement hostname addresses
This is required by quite a few clients, like Chrome. Implement it for
better usability of our userspace network while debugging.
Change-Id: I5db16d3702800b79f88d11c132ce8f7469839ec4
Reviewed-on: https://review.monogon.dev/c/monogon/+/3842
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/osbase/socksproxy/protocol.go b/osbase/socksproxy/protocol.go
index 0d5d133..698d324 100644
--- a/osbase/socksproxy/protocol.go
+++ b/osbase/socksproxy/protocol.go
@@ -115,9 +115,13 @@
}
var addrBytes []byte
+ var hostnameBytes []byte
switch header.Atyp {
case 1:
addrBytes = make([]byte, 4)
+ case 3:
+ // Variable-length string to resolve
+ addrBytes = make([]byte, 1)
case 4:
addrBytes = make([]byte, 16)
default:
@@ -127,20 +131,30 @@
return nil, fmt.Errorf("when reading address: %w", err)
}
+ // Handle domain name addressing, required by for example Chrome
+ if header.Atyp == 3 {
+ hostnameBytes = make([]byte, addrBytes[0])
+ if _, err := io.ReadFull(r, hostnameBytes); err != nil {
+ return nil, fmt.Errorf("when reading address: %w", err)
+ }
+ }
+
var port uint16
if err := binary.Read(r, binary.BigEndian, &port); err != nil {
return nil, fmt.Errorf("when reading port: %w", err)
}
return &connectRequest{
- address: addrBytes,
- port: port,
+ address: addrBytes,
+ hostname: string(hostnameBytes),
+ port: port,
}, nil
}
type connectRequest struct {
- address net.IP
- port uint16
+ address net.IP
+ hostname string
+ port uint16
}
// Reply is an RFC1928 6. “Replies” reply field value. It's returned to the
diff --git a/osbase/socksproxy/socksproxy.go b/osbase/socksproxy/socksproxy.go
index 47a398c..6143f90 100644
--- a/osbase/socksproxy/socksproxy.go
+++ b/osbase/socksproxy/socksproxy.go
@@ -52,6 +52,10 @@
// This address might be invalid/malformed/internal, and the Connect method
// should sanitize it before using it.
Address net.IP
+ // Hostname is a string that the client requested to connect to. Only set if
+ // Address is empty. Format and resolution rules are up to the implementer,
+ // a lot of clients only allow valid DNS labels.
+ Hostname string
// Port is the TCP port number that the client requested to connect to.
Port uint16
}
@@ -105,7 +109,13 @@
func (h *hostHandler) Connect(ctx context.Context, req *ConnectRequest) *ConnectResponse {
port := fmt.Sprintf("%d", req.Port)
- addr := net.JoinHostPort(req.Address.String(), port)
+ var host string
+ if req.Hostname != "" {
+ host = req.Hostname
+ } else {
+ host = req.Address.String()
+ }
+ addr := net.JoinHostPort(host, port)
s, err := net.Dial("tcp", addr)
if err != nil {
log.Printf("HostHandler could not dial %q: %v", addr, err)
@@ -192,8 +202,9 @@
// Ask handler.Connect for a backend.
conRes := handler.Connect(ctxR, &ConnectRequest{
- Address: req.address,
- Port: req.port,
+ Address: req.address,
+ Hostname: req.hostname,
+ Port: req.port,
})
// Handle programming error when returned value is nil.
if conRes == nil {