pax_global_header00006660000000000000000000000064137553361030014520gustar00rootroot0000000000000052 comment=2ba2a7cd5c7b770795f33369ad00a50338ec6869 gateway-1.0.7/000077500000000000000000000000001375533610300131665ustar00rootroot00000000000000gateway-1.0.7/.gitignore000066400000000000000000000000161375533610300151530ustar00rootroot00000000000000.vscode .idea gateway-1.0.7/.travis.yml000066400000000000000000000002411375533610300152740ustar00rootroot00000000000000language: go go: - 1.6.2 - tip allowed_failures: - go: tip install: - go get -d -v ./... && go install -race -v ./... script: go test -race -v ./... gateway-1.0.7/LICENSE000066400000000000000000000030241375533610300141720ustar00rootroot00000000000000// Copyright (c) 2010 Jack Palevich. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gateway-1.0.7/README.md000066400000000000000000000006051375533610300144460ustar00rootroot00000000000000# gateway A simple library for discovering the IP address of the default gateway. [![Build Status](https://travis-ci.org/jackpal/gateway.svg)](https://travis-ci.org/jackpal/gateway) Provides implementations for: + FreeBSD + Linux + OS X (Darwin) + Solaris + Windows Other platforms use an implementation that always returns an error. Pull requests for other OSs happily considered! gateway-1.0.7/gateway.go000066400000000000000000000012051375533610300151540ustar00rootroot00000000000000package gateway import ( "errors" "net" "runtime" ) var ( errNoGateway = errors.New("no gateway found") errCantParse = errors.New("can't parse string output") errNotImplemented = errors.New("not implemented for OS: " + runtime.GOOS) ) // DiscoverGateway is the OS independent function to get the default gateway func DiscoverGateway() (ip net.IP, err error) { return discoverGatewayOSSpecific() } // DiscoverInterface is the OS independent function to call to get the default network interface IP that uses the default gateway func DiscoverInterface() (ip net.IP, err error) { return discoverGatewayInterfaceOSSpecific() } gateway-1.0.7/gateway_darwin.go000066400000000000000000000006211375533610300165210ustar00rootroot00000000000000// +build darwin package gateway import ( "net" "os/exec" ) func discoverGatewayOSSpecific() (net.IP, error) { routeCmd := exec.Command("/sbin/route", "-n", "get", "0.0.0.0") output, err := routeCmd.CombinedOutput() if err != nil { return nil, err } return parseDarwinRouteGet(output) } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { return nil, errNotImplemented } gateway-1.0.7/gateway_freebsd.go000066400000000000000000000006071375533610300166530ustar00rootroot00000000000000// +build freebsd package gateway import ( "net" "os/exec" ) func discoverGatewayOSSpecific() (ip net.IP, err error) { routeCmd := exec.Command("netstat", "-rn") output, err := routeCmd.CombinedOutput() if err != nil { return nil, err } return parseBSDSolarisNetstat(output) } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { return nil, errNotImplemented } gateway-1.0.7/gateway_linux.go000066400000000000000000000014441375533610300164000ustar00rootroot00000000000000// +build linux package gateway import ( "fmt" "io/ioutil" "net" "os" ) const ( // See http://man7.org/linux/man-pages/man8/route.8.html file = "/proc/net/route" ) func discoverGatewayOSSpecific() (ip net.IP, err error) { f, err := os.Open(file) if err != nil { return nil, fmt.Errorf("Can't access %s", file) } defer f.Close() bytes, err := ioutil.ReadAll(f) if err != nil { return nil, fmt.Errorf("Can't read %s", file) } return parseLinuxGatewayIP(bytes) } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { f, err := os.Open(file) if err != nil { return nil, fmt.Errorf("Can't access %s", file) } defer f.Close() bytes, err := ioutil.ReadAll(f) if err != nil { return nil, fmt.Errorf("Can't read %s", file) } return parseLinuxInterfaceIP(bytes) } gateway-1.0.7/gateway_parsers.go000066400000000000000000000166431375533610300167270ustar00rootroot00000000000000package gateway import ( "bufio" "bytes" "encoding/binary" "errors" "fmt" "net" "strconv" "strings" ) type windowsRouteStruct struct { Destination string Netmask string Gateway string Interface string Metric string } type linuxRouteStruct struct { Iface string Destination string Gateway string Flags string RefCnt string Use string Metric string Mask string MTU string Window string IRTT string } func parseToWindowsRouteStruct(output []byte) (windowsRouteStruct, error) { // Windows route output format is always like this: // =========================================================================== // Interface List // 8 ...00 12 3f a7 17 ba ...... Intel(R) PRO/100 VE Network Connection // 1 ........................... Software Loopback Interface 1 // =========================================================================== // IPv4 Route Table // =========================================================================== // Active Routes: // Network Destination Netmask Gateway Interface Metric // 0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.100 20 // =========================================================================== // // Windows commands are localized, so we can't just look for "Active Routes:" string // I'm trying to pick the active route, // then jump 2 lines and get the row // Not using regex because output is quite standard from Windows XP to 8 (NEEDS TESTING) lines := strings.Split(string(output), "\n") sep := 0 for idx, line := range lines { if sep == 3 { // We just entered the 2nd section containing "Active Routes:" if len(lines) <= idx+2 { return windowsRouteStruct{}, errNoGateway } fields := strings.Fields(lines[idx+2]) if len(fields) < 5 { return windowsRouteStruct{}, errCantParse } return windowsRouteStruct{ Destination: fields[0], Netmask: fields[1], Gateway: fields[2], Interface: fields[3], Metric: fields[4], }, nil } if strings.HasPrefix(line, "=======") { sep++ continue } } return windowsRouteStruct{}, errNoGateway } func parseToLinuxRouteStruct(output []byte) (linuxRouteStruct, error) { // parseLinuxProcNetRoute parses the route file located at /proc/net/route // and returns the IP address of the default gateway. The default gateway // is the one with Destination value of 0.0.0.0. // // The Linux route file has the following format: // // $ cat /proc/net/route // // Iface Destination Gateway Flags RefCnt Use Metric Mask // eno1 00000000 C900A8C0 0003 0 0 100 00000000 0 00 // eno1 0000A8C0 00000000 0001 0 0 100 00FFFFFF 0 00 const ( sep = "\t" // field separator destinationField = 1 // field containing hex destination address gatewayField = 2 // field containing hex gateway address ) scanner := bufio.NewScanner(bytes.NewReader(output)) // Skip header line if !scanner.Scan() { return linuxRouteStruct{}, errors.New("Invalid linux route file") } for scanner.Scan() { row := scanner.Text() tokens := strings.Split(row, sep) if len(tokens) < 11 { return linuxRouteStruct{}, fmt.Errorf("invalid row '%s' in route file: doesn't have 11 fields", row) } // Cast hex destination address to int destinationHex := "0x" + tokens[destinationField] destination, err := strconv.ParseInt(destinationHex, 0, 64) if err != nil { return linuxRouteStruct{}, fmt.Errorf( "parsing destination field hex '%s' in row '%s': %w", destinationHex, row, err, ) } // The default interface is the one that's 0 if destination != 0 { continue } return linuxRouteStruct{ Iface: tokens[0], Destination: tokens[1], Gateway: tokens[2], Flags: tokens[3], RefCnt: tokens[4], Use: tokens[5], Metric: tokens[6], Mask: tokens[7], MTU: tokens[8], Window: tokens[9], IRTT: tokens[10], }, nil } return linuxRouteStruct{}, errors.New("interface with default destination not found") } func parseWindowsGatewayIP(output []byte) (net.IP, error) { parsedOutput, err := parseToWindowsRouteStruct(output) if err != nil { return nil, err } ip := net.ParseIP(parsedOutput.Gateway) if ip == nil { return nil, errCantParse } return ip, nil } func parseWindowsInterfaceIP(output []byte) (net.IP, error) { parsedOutput, err := parseToWindowsRouteStruct(output) if err != nil { return nil, err } ip := net.ParseIP(parsedOutput.Interface) if ip == nil { return nil, errCantParse } return ip, nil } func parseLinuxGatewayIP(output []byte) (net.IP, error) { parsedStruct, err := parseToLinuxRouteStruct(output) if err != nil { return nil, err } destinationHex := "0x" + parsedStruct.Destination gatewayHex := "0x" + parsedStruct.Gateway // cast hex address to uint32 d, err := strconv.ParseInt(gatewayHex, 0, 64) if err != nil { return nil, fmt.Errorf( "parsing default interface address field hex '%s': %w", destinationHex, err, ) } // make net.IP address from uint32 ipd32 := make(net.IP, 4) binary.LittleEndian.PutUint32(ipd32, uint32(d)) // format net.IP to dotted ipV4 string return net.IP(ipd32), nil } func parseLinuxInterfaceIP(output []byte) (net.IP, error) { parsedStruct, err := parseToLinuxRouteStruct(output) if err != nil { return nil, err } iface, err := net.InterfaceByName(parsedStruct.Iface) if err != nil { return nil, err } addrs, err := iface.Addrs() if err != nil { return nil, err } // split when its 192.168.8.8/24 ipString := strings.Split(addrs[0].String(), "/")[0] ip := net.ParseIP(ipString) if ip == nil { return nil, fmt.Errorf("invalid addr %s", ipString) } return ip, nil } func parseDarwinRouteGet(output []byte) (net.IP, error) { // Darwin route out format is always like this: // route to: default // destination: default // mask: default // gateway: 192.168.1.1 lines := strings.Split(string(output), "\n") for _, line := range lines { fields := strings.Fields(line) if len(fields) >= 2 && fields[0] == "gateway:" { ip := net.ParseIP(fields[1]) if ip != nil { return ip, nil } } } return nil, errNoGateway } func parseBSDSolarisNetstat(output []byte) (net.IP, error) { // netstat -rn produces the following on FreeBSD: // Routing tables // // Internet: // Destination Gateway Flags Netif Expire // default 10.88.88.2 UGS em0 // 10.88.88.0/24 link#1 U em0 // 10.88.88.148 link#1 UHS lo0 // 127.0.0.1 link#2 UH lo0 // // Internet6: // Destination Gateway Flags Netif Expire // ::/96 ::1 UGRS lo0 // ::1 link#2 UH lo0 // ::ffff:0.0.0.0/96 ::1 UGRS lo0 // fe80::/10 ::1 UGRS lo0 // ... outputLines := strings.Split(string(output), "\n") for _, line := range outputLines { fields := strings.Fields(line) if len(fields) >= 2 && fields[0] == "default" { ip := net.ParseIP(fields[1]) if ip != nil { return ip, nil } } } return nil, errNoGateway } gateway-1.0.7/gateway_solaris.go000066400000000000000000000006071375533610300167150ustar00rootroot00000000000000// +build solaris package gateway import ( "net" "os/exec" ) func discoverGatewayOSSpecific() (ip net.IP, err error) { routeCmd := exec.Command("netstat", "-rn") output, err := routeCmd.CombinedOutput() if err != nil { return nil, err } return parseBSDSolarisNetstat(output) } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { return nil, errNotImplemented } gateway-1.0.7/gateway_test.go000066400000000000000000000234041375533610300162200ustar00rootroot00000000000000package gateway import ( "net" "testing" ) type testcase struct { output []byte ok bool gateway string } func TestParseWindows(t *testing.T) { correctData := []byte(` =========================================================================== Interface List 8 ...00 12 3f a7 17 ba ...... Intel(R) PRO/100 VE Network Connection 1 ........................... Software Loopback Interface 1 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: Network Destination Netmask Gateway Interface Metric 0.0.0.0 0.0.0.0 10.88.88.2 10.88.88.149 10 =========================================================================== Persistent Routes: `) localizedData := []byte( `=========================================================================== Liste d'Interfaces 17...00 28 f8 39 61 6b ......Microsoft Wi-Fi Direct Virtual Adapter 1...........................Software Loopback Interface 1 =========================================================================== IPv4 Table de routage =========================================================================== Itinéraires actifs : Destination réseau Masque réseau Adr. passerelle Adr. interface Métrique 0.0.0.0 0.0.0.0 10.88.88.2 10.88.88.149 10 =========================================================================== Itinéraires persistants : Aucun `) randomData := []byte(` Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation `) noRoute := []byte(` =========================================================================== Interface List 8 ...00 12 3f a7 17 ba ...... Intel(R) PRO/100 VE Network Connection 1 ........................... Software Loopback Interface 1 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: `) badRoute1 := []byte(` =========================================================================== Interface List 8 ...00 12 3f a7 17 ba ...... Intel(R) PRO/100 VE Network Connection 1 ........................... Software Loopback Interface 1 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: =========================================================================== Persistent Routes: `) badRoute2 := []byte(` =========================================================================== Interface List 8 ...00 12 3f a7 17 ba ...... Intel(R) PRO/100 VE Network Connection 1 ........................... Software Loopback Interface 1 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: Network Destination Netmask Gateway Interface Metric 0.0.0.0 0.0.0.0 foo 10.88.88.149 10 =========================================================================== Persistent Routes: `) testcases := []testcase{ {correctData, true, "10.88.88.2"}, {localizedData, true, "10.88.88.2"}, {randomData, false, ""}, {noRoute, false, ""}, {badRoute1, false, ""}, {badRoute2, false, ""}, } test(t, testcases, parseWindowsGatewayIP) interfaceTestCases := []testcase{ {correctData, true, "10.88.88.149"}, {localizedData, true, "10.88.88.149"}, {randomData, false, ""}, {noRoute, false, ""}, {badRoute1, false, ""}, {badRoute2, true, "10.88.88.149"}, } test(t, interfaceTestCases, parseWindowsInterfaceIP) } func TestParseLinux(t *testing.T) { correctData := []byte(`Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT wlp4s0 0000FEA9 00000000 0001 0 0 1000 0000FFFF 0 0 0 docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 docker_gwbridge 000012AC 00000000 0001 0 0 0 0000FFFF 0 0 0 wlp4s0 0008A8C0 00000000 0001 0 0 600 00FFFFFF 0 0 0 wlp4s0 00000000 0108A8C0 0003 0 0 600 00000000 0 0 0 `) noRoute := []byte(` Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT `) testcases := []testcase{ {correctData, true, "192.168.8.1"}, {noRoute, false, ""}, } test(t, testcases, parseLinuxGatewayIP) // ifData := []byte(`Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT // eth0 00000000 00000000 0001 0 0 1000 0000FFFF 0 0 0 // `) // interfaceTestCases := []testcase{ // {ifData, true, "192.168.8.238"}, // {noRoute, false, ""}, // } // to run interface test in your local computer, change eth0 with your default interface name, and change the expected IP to be your default IP // test(t, interfaceTestCases, parseLinuxInterfaceIP) } func TestParseBSDSolarisNetstat(t *testing.T) { correctDataFreeBSD := []byte(` Routing tables Internet: Destination Gateway Flags Netif Expire default 10.88.88.2 UGS em0 10.88.88.0/24 link#1 U em0 10.88.88.148 link#1 UHS lo0 127.0.0.1 link#2 UH lo0 Internet6: Destination Gateway Flags Netif Expire ::/96 ::1 UGRS lo0 ::1 link#2 UH lo0 ::ffff:0.0.0.0/96 ::1 UGRS lo0 fe80::/10 ::1 UGRS lo0 `) correctDataSolaris := []byte(` Routing Table: IPv4 Destination Gateway Flags Ref Use Interface -------------------- -------------------- ----- ----- ---------- --------- default 172.16.32.1 UG 2 76419 net0 127.0.0.1 127.0.0.1 UH 2 36 lo0 172.16.32.0 172.16.32.17 U 4 8100 net0 Routing Table: IPv6 Destination/Mask Gateway Flags Ref Use If --------------------------- --------------------------- ----- --- ------- ----- ::1 ::1 UH 3 75382 lo0 2001:470:deeb:32::/64 2001:470:deeb:32::17 U 3 2744 net0 fe80::/10 fe80::6082:52ff:fedc:7df0 U 3 8430 net0 `) randomData := []byte(` Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation `) noRoute := []byte(` Internet: Destination Gateway Flags Netif Expire 10.88.88.0/24 link#1 U em0 10.88.88.148 link#1 UHS lo0 127.0.0.1 link#2 UH lo0 `) badRoute := []byte(` Internet: Destination Gateway Flags Netif Expire default foo UGS em0 10.88.88.0/24 link#1 U em0 10.88.88.148 link#1 UHS lo0 127.0.0.1 link#2 UH lo0 `) testcases := []testcase{ {correctDataFreeBSD, true, "10.88.88.2"}, {correctDataSolaris, true, "172.16.32.1"}, {randomData, false, ""}, {noRoute, false, ""}, {badRoute, false, ""}, } test(t, testcases, parseBSDSolarisNetstat) } func TestParseDarwinRouteGet(t *testing.T) { correctData := []byte(` route to: 0.0.0.0 destination: default mask: default gateway: 172.16.32.1 interface: en0 flags: recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire 0 0 0 0 0 0 1500 0 `) randomData := []byte(` test Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation `) noRoute := []byte(` route to: 0.0.0.0 destination: default mask: default `) badRoute := []byte(` route to: 0.0.0.0 destination: default mask: default gateway: foo interface: en0 flags: recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire 0 0 0 0 0 0 1500 0 `) testcases := []testcase{ {correctData, true, "172.16.32.1"}, {randomData, false, ""}, {noRoute, false, ""}, {badRoute, false, ""}, } test(t, testcases, parseDarwinRouteGet) } func test(t *testing.T, testcases []testcase, fn func([]byte) (net.IP, error)) { for i, tc := range testcases { net, err := fn(tc.output) if tc.ok { if err != nil { t.Errorf("Unexpected error in test #%d: %v", i, err) } if net.String() != tc.gateway { t.Errorf("Unexpected gateway address %v != %s", net, tc.gateway) } } else if err == nil { t.Errorf("Unexpected nil error in test #%d", i) } } } gateway-1.0.7/gateway_unimplemented.go000066400000000000000000000004301375533610300201010ustar00rootroot00000000000000// +build !darwin,!linux,!windows,!solaris,!freebsd package gateway import ( "net" ) func discoverGatewayOSSpecific() (ip net.IP, err error) { return ip, errNotImplemented } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { return nil, errNotImplemented } gateway-1.0.7/gateway_windows.go000066400000000000000000000012531375533610300167310ustar00rootroot00000000000000// +build windows package gateway import ( "net" "os/exec" "syscall" ) func discoverGatewayOSSpecific() (ip net.IP, err error) { routeCmd := exec.Command("route", "print", "0.0.0.0") routeCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} output, err := routeCmd.CombinedOutput() if err != nil { return nil, err } return parseWindowsGatewayIP(output) } func discoverGatewayInterfaceOSSpecific() (ip net.IP, err error) { routeCmd := exec.Command("route", "print", "0.0.0.0") routeCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} output, err := routeCmd.CombinedOutput() if err != nil { return nil, err } return parseWindowsInterfaceIP(output) } gateway-1.0.7/go.mod000066400000000000000000000000531375533610300142720ustar00rootroot00000000000000module github.com/jackpal/gateway go 1.14