summaryrefslogtreecommitdiff
path: root/obfsproxy/main.go
diff options
context:
space:
mode:
authoratanarjuat <atanarjuat@example.com>2022-05-21 00:43:41 +0200
committeratanarjuat <atanarjuat@example.com>2022-05-21 05:04:06 +0200
commita809112a20b31b9a4adca31ae564d943a41e9023 (patch)
tree8cc8b641a25867a8c556a073f44c25e1ff36433d /obfsproxy/main.go
parentcefa2c334751efbf1d4b479fb827c21c5b801a71 (diff)
functional transparent proxy
Diffstat (limited to 'obfsproxy/main.go')
-rw-r--r--obfsproxy/main.go154
1 files changed, 122 insertions, 32 deletions
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
+ }
}