diff --git a/x/config/config.go b/x/config/config.go index 0dfe0a44..c1821b2a 100644 --- a/x/config/config.go +++ b/x/config/config.go @@ -138,5 +138,6 @@ func NewpacketListener(transportConfig string) (transport.PacketListener, error) return nil, errors.New("config scheme must be 'ss' for a PacketListener") } + // todo: support nested dialer, the last part must be "ss://" return newShadowsocksPacketListenerFromURL(url) } diff --git a/x/config/shadowsocks.go b/x/config/shadowsocks.go index c1c74818..6b4a3ddf 100644 --- a/x/config/shadowsocks.go +++ b/x/config/shadowsocks.go @@ -60,6 +60,7 @@ func newShadowsocksPacketListenerFromURL(configURL *url.URL) (transport.PacketLi if err != nil { return nil, err } + // todo: accept an inner dialer from the caller and pass it to UDPEndpoint ep := &transport.UDPEndpoint{Address: config.serverAddress} return shadowsocks.NewPacketListener(ep, config.cryptoKey) } diff --git a/x/examples/outline-cli/README.md b/x/examples/outline-cli/README.md index e0dfb8a1..b3d2b6ba 100644 --- a/x/examples/outline-cli/README.md +++ b/x/examples/outline-cli/README.md @@ -1,6 +1,6 @@ # OutlineVPN CLI -The CLI interface of OutlineVPN client for Linux. +A CLI interface of Outline VPN client for Linux. ### Usage diff --git a/x/examples/outline-cli/app_linux.go b/x/examples/outline-cli/app_linux.go index 858ffcfd..9b35675e 100644 --- a/x/examples/outline-cli/app_linux.go +++ b/x/examples/outline-cli/app_linux.go @@ -17,7 +17,6 @@ package main import ( "fmt" "io" - "log" "os" "os/signal" "sync" @@ -30,7 +29,7 @@ func (app App) Run() error { trafficCopyWg := &sync.WaitGroup{} defer trafficCopyWg.Wait() - tun, err := newTunDevice(app.RoutingConfig) + tun, err := newTunDevice(app.RoutingConfig.TunDeviceName, app.RoutingConfig.TunDeviceIP) if err != nil { return fmt.Errorf("failed to create tun device: %w", err) } @@ -56,12 +55,12 @@ func (app App) Run() error { go func() { defer trafficCopyWg.Done() written, err := io.Copy(ss, tun) - log.Printf("[info] tun -> OutlineDevice stopped: %v %v\n", written, err) + logging.Info.Printf("tun -> OutlineDevice stopped: %v %v\n", written, err) }() go func() { defer trafficCopyWg.Done() written, err := io.Copy(tun, ss) - log.Printf("[info] OutlineDevice -> tun stopped: %v %v\n", written, err) + logging.Info.Printf("OutlineDevice -> tun stopped: %v %v\n", written, err) }() if err := setSystemDNSServer(app.RoutingConfig.DNSServerIP); err != nil { @@ -77,6 +76,6 @@ func (app App) Run() error { sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, unix.SIGTERM, unix.SIGHUP) s := <-sigc - log.Printf("received %v, terminating...\n", s) + logging.Info.Printf("received %v, terminating...\n", s) return nil } diff --git a/x/examples/outline-cli/app_other.go b/x/examples/outline-cli/app_other.go index a7a67ba7..ca351f30 100644 --- a/x/examples/outline-cli/app_other.go +++ b/x/examples/outline-cli/app_other.go @@ -16,9 +16,8 @@ package main -import "fmt" +import "errors" func (App) Run() error { - fmt.Println("OutlineVPN does not support this platform") - return nil + return errors.New("platform not supported") } diff --git a/x/examples/outline-cli/dns_linux.go b/x/examples/outline-cli/dns_linux.go index 01f26f31..bb34aee8 100644 --- a/x/examples/outline-cli/dns_linux.go +++ b/x/examples/outline-cli/dns_linux.go @@ -16,10 +16,10 @@ package main import ( "fmt" - "log" "os" ) +// todo: find a more portable way of configuring DNS (e.g. resolved) const ( resolvConfFile = "/etc/resolv.conf" resolvConfHeadFile = "/etc/resolv.conf.head" @@ -55,12 +55,12 @@ func backupAndWriteFile(original, backup string, data []byte) error { func restoreFileIfExists(backup, original string) { if _, err := os.Stat(backup); err != nil { - log.Printf("[warn] no DNS config backup file '%s' presents: %v\n", backup, err) + logging.Warn.Printf("no DNS config backup file '%s' presents: %v\n", backup, err) return } if err := os.Rename(backup, original); err != nil { - log.Printf("[error] failed to restore DNS config from backup '%s' to '%s': %v\n", backup, original, err) + logging.Err.Printf("failed to restore DNS config from backup '%s' to '%s': %v\n", backup, original, err) return } - log.Printf("[info] DNS config restored from '%s' to '%s'\n", backup, original) + logging.Info.Printf("DNS config restored from '%s' to '%s'\n", backup, original) } diff --git a/x/examples/outline-cli/ipv6_linux.go b/x/examples/outline-cli/ipv6_linux.go index 4a02ffe9..5dd245e9 100644 --- a/x/examples/outline-cli/ipv6_linux.go +++ b/x/examples/outline-cli/ipv6_linux.go @@ -16,7 +16,6 @@ package main import ( "fmt" - "log" "os" ) @@ -45,6 +44,6 @@ func enableIPv6(enabled bool) (bool, error) { return prevEnabled, fmt.Errorf("failed to write IPv6 config: %w", err) } - log.Printf("[info] updated global IPv6 support: %v\n", enabled) + logging.Info.Printf("updated global IPv6 support: %v\n", enabled) return prevEnabled, nil } diff --git a/x/examples/outline-cli/main.go b/x/examples/outline-cli/main.go index 65c737d3..1be00964 100644 --- a/x/examples/outline-cli/main.go +++ b/x/examples/outline-cli/main.go @@ -17,9 +17,20 @@ package main import ( "flag" "fmt" + "io" "log" + "os" ) +var logging = &struct { + Debug, Info, Warn, Err *log.Logger +}{ + Debug: log.New(io.Discard, "[DEBUG] ", log.LstdFlags), + Info: log.New(os.Stdout, "[INFO] ", log.LstdFlags), + Warn: log.New(os.Stderr, "[WARN] ", log.LstdFlags), + Err: log.New(os.Stderr, "[ERROR] ", log.LstdFlags), +} + // ./app -transport "ss://..." func main() { fmt.Println("OutlineVPN CLI (experimental)") @@ -39,6 +50,6 @@ func main() { flag.Parse() if err := app.Run(); err != nil { - log.Printf("[error] %v\n", err) + logging.Err.Printf("%v\n", err) } } diff --git a/x/examples/outline-cli/outline_packet_proxy.go b/x/examples/outline-cli/outline_packet_proxy.go index d9b24a9a..4ed6be1d 100644 --- a/x/examples/outline-cli/outline_packet_proxy.go +++ b/x/examples/outline-cli/outline_packet_proxy.go @@ -17,7 +17,6 @@ package main import ( "context" "fmt" - "log" "github.com/Jigsaw-Code/outline-sdk/network" "github.com/Jigsaw-Code/outline-sdk/network/dnstruncate" @@ -58,10 +57,10 @@ func (proxy *outlinePacketProxy) testConnectivityAndRefresh(resolver, domain str _, err := connectivity.TestResolverPacketConnectivity(context.Background(), dnsResolver, domain) if err != nil { - log.Println("[info] remote server cannot handle UDP traffic, switch to DNS truncate mode") + logging.Info.Println("remote server cannot handle UDP traffic, switch to DNS truncate mode") return proxy.SetProxy(proxy.fallback) } else { - log.Println("[info] remote server supports UDP, we will delegate all UDP packets to it") + logging.Info.Println("remote server supports UDP, we will delegate all UDP packets to it") return proxy.SetProxy(proxy.remote) } } diff --git a/x/examples/outline-cli/routing_linux.go b/x/examples/outline-cli/routing_linux.go index e6bc6b09..e690fd7c 100644 --- a/x/examples/outline-cli/routing_linux.go +++ b/x/examples/outline-cli/routing_linux.go @@ -17,7 +17,6 @@ package main import ( "errors" "fmt" - "log" "net" "github.com/vishvananda/netlink" @@ -34,10 +33,10 @@ func startRouting(proxyIP string, config *RoutingConfig) error { func stopRouting(routingTable int) { if err := cleanUpRoutingTable(routingTable); err != nil { - log.Printf("[error] failed to clean up routing table '%v': %v\n", routingTable, err) + logging.Err.Printf("failed to clean up routing table '%v': %v\n", routingTable, err) } if err := cleanUpRule(); err != nil { - log.Printf("[error] failed to clean up IP rule: %v\n", err) + logging.Err.Printf("failed to clean up IP rule: %v\n", err) } } @@ -63,7 +62,7 @@ func setupRoutingTable(routingTable int, tunName, gwSubnet string, tunIP string) if err = netlink.RouteAdd(&r); err != nil { return fmt.Errorf("failed to add routing entry '%v' -> '%v': %w", r.Src, r.Dst, err) } - log.Printf("[info] routing traffic from %v to %v through nic %v\n", r.Src, r.Dst, r.LinkIndex) + logging.Info.Printf("routing traffic from %v to %v through nic %v\n", r.Src, r.Dst, r.LinkIndex) r = netlink.Route{ LinkIndex: tun.Attrs().Index, @@ -74,7 +73,7 @@ func setupRoutingTable(routingTable int, tunName, gwSubnet string, tunIP string) if err := netlink.RouteAdd(&r); err != nil { return fmt.Errorf("failed to add gateway routing entry '%v': %w", r.Gw, err) } - log.Printf("[info] routing traffic via gw %v through nic %v...\n", r.Gw, r.LinkIndex) + logging.Info.Printf("routing traffic via gw %v through nic %v...\n", r.Gw, r.LinkIndex) return nil } @@ -93,7 +92,7 @@ func cleanUpRoutingTable(routingTable int) error { } } if rtDelErr == nil { - log.Printf("[info] routing table '%v' has been cleaned up\n", routingTable) + logging.Info.Printf("routing table '%v' has been cleaned up\n", routingTable) } return rtDelErr } @@ -114,7 +113,7 @@ func setupIpRule(svrIp string, routingTable, routingPriority int) error { if err := netlink.RuleAdd(ipRule); err != nil { return fmt.Errorf("failed to add IP rule (table %v, dst %v): %w", ipRule.Table, ipRule.Dst, err) } - log.Printf("[info] ip rule 'from all not to %v via table %v' created\n", ipRule.Dst, ipRule.Table) + logging.Info.Printf("ip rule 'from all not to %v via table %v' created\n", ipRule.Dst, ipRule.Table) return nil } @@ -125,7 +124,7 @@ func cleanUpRule() error { if err := netlink.RuleDel(ipRule); err != nil { return fmt.Errorf("failed to delete IP rule of routing table '%v': %w", ipRule.Table, err) } - log.Printf("[info] ip rule of routing table '%v' deleted\n", ipRule.Table) + logging.Info.Printf("ip rule of routing table '%v' deleted\n", ipRule.Table) ipRule = nil return nil } diff --git a/x/examples/outline-cli/tun_device_linux.go b/x/examples/outline-cli/tun_device_linux.go index fad20aa1..bc2da40f 100644 --- a/x/examples/outline-cli/tun_device_linux.go +++ b/x/examples/outline-cli/tun_device_linux.go @@ -25,22 +25,23 @@ import ( type tunDevice struct { *water.Interface + link netlink.Link } var _ network.IPDevice = (*tunDevice)(nil) -func newTunDevice(config *RoutingConfig) (d network.IPDevice, err error) { - if len(config.TunDeviceName) == 0 { +func newTunDevice(name, ip string) (d network.IPDevice, err error) { + if len(name) == 0 { return nil, errors.New("name is required for TUN/TAP device") } - if len(config.TunDeviceIP) == 0 { + if len(ip) == 0 { return nil, errors.New("ip is required for TUN/TAP device") } tun, err := water.New(water.Config{ DeviceType: water.TUN, PlatformSpecificParams: water.PlatformSpecificParams{ - Name: config.TunDeviceName, + Name: name, Persist: false, }, }) @@ -54,9 +55,17 @@ func newTunDevice(config *RoutingConfig) (d network.IPDevice, err error) { } }() - tunDev := &tunDevice{tun} - if err := tunDev.configureSubnetAndBringUp(config.TunDeviceIP); err != nil { - return nil, fmt.Errorf("failed to configure TUN/TAP device: %w", err) + tunLink, err := netlink.LinkByName(name) + if err != nil { + return nil, fmt.Errorf("newly created TUN/TAP device '%s' not found: %w", name, err) + } + + tunDev := &tunDevice{tun, tunLink} + if err := tunDev.configureSubnet(ip); err != nil { + return nil, fmt.Errorf("failed to configure TUN/TAP device subnet: %w", err) + } + if err := tunDev.bringUp(); err != nil { + return nil, fmt.Errorf("failed to bring up TUN/TAP device: %w", err) } return tunDev, nil } @@ -65,22 +74,21 @@ func (d *tunDevice) MTU() int { return 1500 } -func (d *tunDevice) configureSubnetAndBringUp(ip string) error { - tunName := d.Interface.Name() - tunLink, err := netlink.LinkByName(tunName) - if err != nil { - return fmt.Errorf("TUN/TAP device '%s' not found: %w", tunName, err) - } +func (d *tunDevice) configureSubnet(ip string) error { subnet := ip + "/32" addr, err := netlink.ParseAddr(subnet) if err != nil { return fmt.Errorf("subnet address '%s' is not valid: %w", subnet, err) } - if err := netlink.AddrAdd(tunLink, addr); err != nil { - return fmt.Errorf("failed to add subnet to TUN/TAP device '%s': %w", tunName, err) + if err := netlink.AddrAdd(d.link, addr); err != nil { + return fmt.Errorf("failed to add subnet to TUN/TAP device '%s': %w", d.Interface.Name(), err) } - if err := netlink.LinkSetUp(tunLink); err != nil { - return fmt.Errorf("failed to bring TUN/TAP device '%s' up: %w", tunName, err) + return nil +} + +func (d *tunDevice) bringUp() error { + if err := netlink.LinkSetUp(d.link); err != nil { + return fmt.Errorf("failed to bring TUN/TAP device '%s' up: %w", d.Interface.Name(), err) } return nil }