From a809112a20b31b9a4adca31ae564d943a41e9023 Mon Sep 17 00:00:00 2001 From: atanarjuat Date: Sat, 21 May 2022 00:43:41 +0200 Subject: functional transparent proxy --- obfsproxy/main.go | 154 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 122 insertions(+), 32 deletions(-) (limited to 'obfsproxy/main.go') diff --git a/obfsproxy/main.go b/obfsproxy/main.go index a35a26d..5e56789 100644 --- a/obfsproxy/main.go +++ b/obfsproxy/main.go @@ -4,7 +4,9 @@ package main import ( "context" "encoding/json" + "errors" "flag" + "fmt" "io" "log" "net" @@ -12,12 +14,19 @@ import ( "os/signal" "0xacab.org/leap/obfsvpn" - "git.torproject.org/pluggable-transports/goptlib.git" ) const transportName = "obfs4" +type Config struct { + NodeID string `json:"node-id"` + PrivateKey string `json:"private-key"` + PublicKey string `json:"public-key"` + DRBGSeed string `json:"drbg-seed"` + IatMode int `json:"iat-mode"` +} + func main() { // Setup logging. logger := log.New(os.Stderr, "", log.LstdFlags) @@ -48,15 +57,17 @@ func main() { } tcpVPNAddr, err := net.ResolveTCPAddr("tcp", vpnAddr) + log.Println("target:", tcpVPNAddr) if err != nil { logger.Fatalf("error resolving VPN address: %v", err) } + var cfg Config fd, err := os.Open(cfgFile) + log.Println("opening:", cfgFile) if err != nil { logger.Fatalf("error opening config file: %v", err) } - var cfg Config err = json.NewDecoder(fd).Decode(&cfg) if err != nil { logger.Fatalf("error decoding config: %v", err) @@ -67,15 +78,16 @@ func main() { debug.SetOutput(os.Stderr) } + log.Println("config:", cfg) + // Setup graceful shutdown. ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) - listenConfig := obfsvpn.ListenConfig{ - NodeID: cfg.NodeID, - PrivateKey: cfg.PrivateKey, - Seed: cfg.DRBGSeed, - StateDir: stateDir, + listenConfig, err := obfsvpn.NewListenConfig(cfg.NodeID, cfg.PrivateKey, cfg.PublicKey, cfg.DRBGSeed, stateDir) + if err != nil { + logger.Fatalf("error creating listener from config: %v", err) } + log.Println("DEBUG:", listenConfig) ln, err := listenConfig.Listen(ctx, "tcp", addr) if err != nil { logger.Fatalf("error binding to %s: %v", addr, err) @@ -105,44 +117,122 @@ func main() { return } debug.Printf("accepted connection %v…", conn) - go proxyConn(ctx, info, conn, net.Dialer{}, logger, debug) + go proxyConn(ctx, info, conn, logger, debug) } } -func proxyConn(ctx context.Context, info *pt.ServerInfo, conn net.Conn, d net.Dialer, logger, debug *log.Logger) { +// obfsConn is a connection to the obfs4 client that we have accepted. we will dial to the remote contained in info +func proxyConn(ctx context.Context, info *pt.ServerInfo, obfsConn net.Conn, logger, debug *log.Logger) { defer func() { - err := conn.Close() + err := obfsConn.Close() if err != nil { debug.Printf("error closing connection: %v", err) } }() - // TODO: do we actually want to send the USERADDR/TRANSPORT ExtOrPort - // commands? I don't understand how this works, so I'm unsure. - remote, err := pt.DialOr(info, conn.RemoteAddr().String(), transportName) + // FIXME scrub ips in other than debug mode! + log.Println("Dialing:", info.OrAddr) + log.Println("Obfs4 client:", obfsConn.RemoteAddr().String()) + + /* + in the case of Tor, pt.DialOr returns a *net.TCPConn after dialing info.OrAddr. + in the vpn case (or any transparent proxy really), we do use + the pt.DialOr method to simply get a dialer to our upstream VPN remote. + + keeping this terminology is a bit stupid and slightly confusing, instead + we could get the clearConn just by doing: + + s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr) + if err != nil { + return nil, err + } + + that is precisely what the code in ptlib is doing. + + We also aspire at being a generic PT at some point, so perhaps it's better to keep the usage + of DialOr? + + Maybe not, and so we don't need to keep the confusing info struct. + */ + + // we'll refer to the connection to the usptream node as "clearConn", as opposed to the obfuscated conn. + // but for sure openvpn or whatever protocol you wrap has its own layer of encryption :) + + clearConn, err := pt.DialOr(info, obfsConn.RemoteAddr().String(), transportName) + if err != nil { logger.Printf("error dialing remote: %v", err) return } - defer func() { - err := remote.Close() - if err != nil { - debug.Printf("error closing remote connection: %v", err) - } - }() - go func() { - _, err := io.Copy(remote, conn) - if err != nil { - logger.Printf("error proxying client data to remote: %v", err) - return - } - }() - go func() { - _, err := io.Copy(conn, remote) - if err != nil { - logger.Printf("error proxying remote data to client: %v", err) - return + if err = CopyLoop(clearConn, obfsConn); err != nil { + debug.Printf("%s - closed connection: %s", "obfsvpn", err.Error()) + } else { + debug.Printf("%s - closed connection", "obfsvpn") + } +} + +// a stock copy loop. let's not dwell too much on who's client and who's server + +func CopyLoop(left net.Conn, right net.Conn) error { + + fmt.Println("--> Entering copy loop.") + + if left == nil { + fmt.Fprintln(os.Stderr, "--> Copy loop has a nil connection (left).") + return errors.New("copy loop has a nil connection (left)") + } + + if right == nil { + fmt.Fprintln(os.Stderr, "--> Copy loop has a nil connection (right).") + return errors.New("copy loop has a nil connection (right)") + } + + // Note: right is always the pt connection. + lockL := make(chan bool) + lockR := make(chan bool) + errChan := make(chan error) + + go CopyLeftToRight(left, right, lockL, errChan) + go CopyRightToLeft(left, right, lockR, errChan) + + leftUp := true + rightUp := true + + var copyErr error + + for leftUp || rightUp { + select { + case <-lockL: + leftUp = false + case <-lockR: + rightUp = false + case copyErr = <-errChan: + log.Println("Error while copying") } - }() + } + + // XXX better to defer? + left.Close() + right.Close() + + return copyErr +} + +// TODO check for data races + +func CopyLeftToRight(l net.Conn, r net.Conn, ll chan bool, errChan chan error) { + _, e := io.Copy(r, l) + ll <- true + if e != nil { + errChan <- e + } +} + +func CopyRightToLeft(l net.Conn, r net.Conn, lr chan bool, errChan chan error) { + _, e := io.Copy(l, r) + lr <- true + if e != nil { + errChan <- e + } } -- cgit v1.2.3