Skip to content

Commit

Permalink
mvp support of upstream socks5 (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
mosajjal authored Jan 9, 2023
1 parent f564452 commit ea6c102
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 11 deletions.
6 changes: 5 additions & 1 deletion httpproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ func handle80(w http.ResponseWriter, r *http.Request) {
rr.Header.Set("Host", hostPort)
}

transport := http.Transport{
Dial: c.dialer.Dial,
}

// Forward request to origin server
resp, err := http.DefaultTransport.RoundTrip(&rr)
resp, err := transport.RoundTrip(&rr)
if err != nil {
// TODO: Passthru more error information
http.Error(w, "Could not reach origin server", 500)
Expand Down
32 changes: 22 additions & 10 deletions https.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net"

slog "golang.org/x/exp/slog"
"golang.org/x/net/proxy"
)

var httpslog = slog.New(log.Handler().WithAttrs([]slog.Attr{{Key: "service", Value: slog.StringValue("https")}}))
Expand Down Expand Up @@ -77,16 +78,27 @@ func handle443(conn net.Conn) error {
"source_ip", conn.RemoteAddr().String(),
"host", sni,
)
// with the manipulation of the soruce address, we can set the outbound interface
srcAddr := net.TCPAddr{
IP: c.sourceAddr,
Port: 0,
}
target, err := net.DialTCP("tcp", &srcAddr, &net.TCPAddr{IP: rAddr, Port: rPort})
if err != nil {
httpslog.Error("could not connect to target", err)
conn.Close()
return err
var target *net.TCPConn
if c.dialer == proxy.Direct {
// with the manipulation of the soruce address, we can set the outbound interface
srcAddr := net.TCPAddr{
IP: c.sourceAddr,
Port: 0,
}
target, err = net.DialTCP("tcp", &srcAddr, &net.TCPAddr{IP: rAddr, Port: rPort})
if err != nil {
httpslog.Error("could not connect to target", err)
conn.Close()
return err
}
} else {
tmp, err := c.dialer.Dial("tcp", fmt.Sprintf("%s:%d", rAddr, rPort))
if err != nil {
httpslog.Error("could not connect to target", err)
conn.Close()
return err
}
target = tmp.(*net.TCPConn)
}
defer target.Close()
c.proxiedHTTPS.Inc(1)
Expand Down
29 changes: 29 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package main

import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
Expand All @@ -22,12 +24,14 @@ import (

"github.com/miekg/dns"
slog "golang.org/x/exp/slog"
"golang.org/x/net/proxy"
)

type runConfig struct {
BindIP string `json:"bindIP"`
PublicIP string `json:"publicIP"`
UpstreamDNS string `json:"upstreamDNS"`
UpstreamSOCKS5 string `json:"upstreamSOCKS5"`
DomainListPath string `json:"domainListPath"`
DomainListRefreshInterval duration `json:"domainListRefreshInterval"`
BindDNSOverTCP bool `json:"bindDnsOverTcp"`
Expand Down Expand Up @@ -57,6 +61,7 @@ type runConfig struct {
mmdb *maxminddb.Reader

dnsClient DNSClient
dialer proxy.Dialer
sourceAddr net.IP

reverseProxySNI string
Expand Down Expand Up @@ -162,6 +167,7 @@ func getPublicIPInner() (string, error) {
func main() {
flag.StringVar(&c.BindIP, "bindIP", "0.0.0.0", "Bind 443 and 80 to a Specific IP Address. Doesn't apply to DNS Server. DNS Server always listens on 0.0.0.0")
flag.StringVar(&c.UpstreamDNS, "upstreamDNS", "udp://8.8.8.8:53", "Upstream DNS URI. examples: udp://1.1.1.1:53, tcp://1.1.1.1:53, tcp-tls://1.1.1.1:853, https://dns.google/dns-query")
flag.StringVar(&c.UpstreamSOCKS5, "upstreamSOCKS5", "socks5://admin:admin@127.0.0.1:1080", "Use a SOCKS proxy for upstream HTTP/HTTPS traffic.")
flag.StringVar(&c.DomainListPath, "domainListPath", "", "Path to the domain list. eg: /tmp/domainlist.csv")
flag.DurationVar(&c.DomainListRefreshInterval.Duration, "domainListRefreshInterval", 60*time.Minute, "Interval to re-fetch the domain list")
flag.BoolVar(&c.AllDomains, "allDomains", false, "Route all HTTP(s) traffic through the SNI proxy")
Expand Down Expand Up @@ -291,6 +297,29 @@ func main() {

}

if c.UpstreamSOCKS5 != "" {
uri, err := url.Parse(c.UpstreamSOCKS5)
if err != nil {
log.Error("", err)
}
if uri.Scheme != "socks5" {
log.Error("only SOCKS5 is supported", nil)
return
}

log.Info("Using an upstream SOCKS5 proxy", "address", uri.Host)
u := uri.User.Username()
p, _ := uri.User.Password()
socksAuth := proxy.Auth{User: u, Password: p}
c.dialer, err = proxy.SOCKS5("tcp", uri.Host, &socksAuth, proxy.Direct)
if err != nil {
fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err)
os.Exit(1)
}
} else {
c.dialer = proxy.Direct
}

tmp, err := dnsclient.New(c.UpstreamDNS, true)
if err != nil {
log.Error("", err)
Expand Down

0 comments on commit ea6c102

Please sign in to comment.