From 0048d87cad570154a8965515ac6eb1ca304e651a Mon Sep 17 00:00:00 2001 From: octeep Date: Fri, 1 Apr 2022 00:47:14 +0100 Subject: [PATCH] add documentation --- cmd/wireproxy/main.go | 6 ++- config.go | 6 +++ routine.go | 88 ++++++++++++++++++++++++++++--------------- wireguard.go | 3 ++ 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/cmd/wireproxy/main.go b/cmd/wireproxy/main.go index f3b5b9f..110dd2f 100644 --- a/cmd/wireproxy/main.go +++ b/cmd/wireproxy/main.go @@ -11,8 +11,11 @@ import ( "suah.dev/protect" ) +// an argument to denote that this process was spawned by -d const daemonProcess = "daemon-process" +// attempts to pledge and panic if it fails +// this does nothing on non-OpenBSD systems func pledgeOrPanic(promises string) { err := protect.Pledge(promises) if err != nil { @@ -21,6 +24,7 @@ func pledgeOrPanic(promises string) { } func main() { + // only allow standard stdio operation, file reading, networking, and exec pledgeOrPanic("stdio rpath inet dns proc exec") isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess @@ -80,7 +84,7 @@ func main() { return } - // no file access is allowed from now on + // no file access is allowed from now on, only networking pledgeOrPanic("stdio inet dns") tnet, err := wireproxy.StartWireguard(conf.Device) diff --git a/config.go b/config.go index a76f2c8..5af6eb2 100644 --- a/config.go +++ b/config.go @@ -12,6 +12,7 @@ import ( "golang.zx2c4.com/go118/netip" ) +// DeviceConfig contains the information to initiate a wireguard connection type DeviceConfig struct { SelfSecretKey string SelfEndpoint []netip.Addr @@ -160,6 +161,7 @@ func resolveIPPAndPort(addr string) (string, error) { return net.JoinHostPort(ip.String(), port), nil } +// ParseInterface parses the [Interface] section and extract the information into `device` func ParseInterface(cfg *ini.File, device *DeviceConfig) error { sections, err := cfg.SectionsByName("Interface") if len(sections) != 1 || err != nil { @@ -197,6 +199,7 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error { return nil } +// ParsePeer parses the [Peer] section and extract the information into `device` func ParsePeer(cfg *ini.File, device *DeviceConfig) error { sections, err := cfg.SectionsByName("Peer") if len(sections) != 1 || err != nil { @@ -292,6 +295,8 @@ func parseSocks5Config(section *ini.Section) (RoutineSpawner, error) { return config, nil } +// Takes a function that parses an individual section into a config, and apply it on all +// specified sections func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName string, f func(*ini.Section) (RoutineSpawner, error)) error { sections, err := cfg.SectionsByName(sectionName) if err != nil { @@ -310,6 +315,7 @@ func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName return nil } +// ParseConfig takes the path of a configuration file and parses it into Configuration func ParseConfig(path string) (*Configuration, error) { iniOpt := ini.LoadOptions{ Insensitive: true, diff --git a/routine.go b/routine.go index de8463f..e8d156c 100644 --- a/routine.go +++ b/routine.go @@ -4,11 +4,11 @@ import ( "context" "crypto/subtle" "errors" - "fmt" "io" "log" "math/rand" "net" + "os" "strconv" "github.com/armon/go-socks5" @@ -17,20 +17,28 @@ import ( "golang.zx2c4.com/wireguard/tun/netstack" ) +// errorLogger is the logger to print error message +var errorLogger = log.New(os.Stderr, "ERROR: ", log.LstdFlags) + +// CredentialValidator stores the authentication data of a socks5 proxy type CredentialValidator struct { username string password string } +// VirtualTun stores a reference to netstack network and DNS configuration type VirtualTun struct { tnet *netstack.Net systemDNS bool } +// RoutineSpawner spawns a routine (e.g. socks5, tcp static routes) after the configuration is parsed type RoutineSpawner interface { SpawnRoutine(vt *VirtualTun) } +// LookupAddr lookups a hostname. +// DNS traffic may or may not be routed depending on VirtualTun's setting func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, error) { if d.systemDNS { return net.DefaultResolver.LookupHost(ctx, name) @@ -39,23 +47,15 @@ func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, erro } } +// ResolveAddrPort resolves a hostname and returns an AddrPort. +// DNS traffic may or may not be routed depending on VirtualTun's setting func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) { name, sport, err := net.SplitHostPort(saddr) if err != nil { return nil, err } - addrs, err := d.LookupAddr(context.Background(), name) - if err != nil { - return nil, err - } - - size := len(addrs) - if size == 0 { - return nil, errors.New("no address found for: " + name) - } - - addr, err := netip.ParseAddr(addrs[rand.Intn(size)]) + addr, err := d.ResolveAddrWithContext(context.Background(), name) if err != nil { return nil, err } @@ -65,34 +65,54 @@ func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) { return nil, &net.OpError{Op: "dial", Err: errors.New("port must be numeric")} } - addrPort := netip.AddrPortFrom(addr, uint16(port)) + addrPort := netip.AddrPortFrom(*addr, uint16(port)) return &addrPort, nil } -func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { - var addrs []string - var err error - - addrs, err = d.LookupAddr(ctx, name) - +// ResolveAddrPort resolves a hostname and returns an AddrPort. +// DNS traffic may or may not be routed depending on VirtualTun's setting +func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*netip.Addr, error) { + addrs, err := d.LookupAddr(ctx, name) if err != nil { - return ctx, nil, err + return nil, err } size := len(addrs) if size == 0 { - return ctx, nil, errors.New("no address found for: " + name) + return nil, errors.New("no address found for: " + name) } - addr := addrs[rand.Intn(size)] - ip := net.ParseIP(addr) - if ip == nil { - return ctx, nil, errors.New("invalid address: " + addr) + rand.Shuffle(size, func(i, j int) { + addrs[i], addrs[j] = addrs[j], addrs[i] + }) + + var addr netip.Addr + for _, saddr := range addrs { + addr, err = netip.ParseAddr(saddr) + if err == nil { + break + } } - return ctx, ip, err + if err != nil { + return nil, err + } + + return &addr, nil } +// ResolveAddrPort resolves a hostname and returns an IP. +// DNS traffic may or may not be routed depending on VirtualTun's setting +func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { + addr, err := d.ResolveAddrWithContext(ctx, name) + if err != nil { + return nil, nil, err + } + + return ctx, addr.AsSlice(), nil +} + +// Spawns a socks5 server. func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) { conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt} if username := config.Username; username != "" { @@ -110,25 +130,30 @@ func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) { } } +// Valid checks the authentication data in CredentialValidator and compare them +// to username and password in constant time. func (c CredentialValidator) Valid(username, password string) bool { u := subtle.ConstantTimeCompare([]byte(c.username), []byte(username)) p := subtle.ConstantTimeCompare([]byte(c.password), []byte(password)) return u&p == 1 } +// connForward copy data from `from` to `to`, then close both stream. func connForward(bufSize int, from io.ReadWriteCloser, to io.ReadWriteCloser) { buf := make([]byte, bufSize) _, err := io.CopyBuffer(to, from, buf) if err != nil { - to.Close() - return + errorLogger.Printf("Cannot forward traffic: %s\n", err.Error()) } + _ = from.Close() + _ = to.Close() } +// tcpClientForward starts a new connection via wireguard and forward traffic from `conn` func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) { sconn, err := tnet.DialTCP(target) if err != nil { - fmt.Printf("[ERROR] TCP Client Tunnel to %s: %s\n", target, err.Error()) + errorLogger.Printf("TCP Client Tunnel to %s: %s\n", target, err.Error()) return } @@ -136,6 +161,7 @@ func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) { go connForward(1024, conn, sconn) } +// Spawns a local TCP server which acts as a proxy to the specified target func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) { raddr, err := vt.ResolveAddrPort(conf.Target) if err != nil { @@ -157,10 +183,11 @@ func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) { } } +// tcpServerForward starts a new connection locally and forward traffic from `conn` func tcpServerForward(target *net.TCPAddr, conn net.Conn) { sconn, err := net.DialTCP("tcp", nil, target) if err != nil { - fmt.Printf("[ERROR] TCP Server Tunnel to %s: %s\n", target, err.Error()) + errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error()) return } @@ -168,6 +195,7 @@ func tcpServerForward(target *net.TCPAddr, conn net.Conn) { go connForward(1024, conn, sconn) } +// Spawns a TCP server on wireguard which acts as a proxy to the specified target func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) { raddr, err := vt.ResolveAddrPort(conf.Target) if err != nil { diff --git a/wireguard.go b/wireguard.go index e465da2..db38ca3 100644 --- a/wireguard.go +++ b/wireguard.go @@ -9,6 +9,7 @@ import ( "golang.zx2c4.com/wireguard/tun/netstack" ) +// DeviceSetting contains the parameters for setting up a tun interface type DeviceSetting struct { ipcRequest string dns []netip.Addr @@ -16,6 +17,7 @@ type DeviceSetting struct { mtu int } +// serialize the config into an IPC request and DeviceSetting func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) { request := fmt.Sprintf(`private_key=%s public_key=%s @@ -28,6 +30,7 @@ allowed_ip=0.0.0.0/0`, conf.SelfSecretKey, conf.PeerPublicKey, conf.PeerEndpoint return setting, nil } +// StartWireguard creates a tun interface on netstack given a configuration func StartWireguard(conf *DeviceConfig) (*VirtualTun, error) { setting, err := createIPCRequest(conf) if err != nil {