This commit is contained in:
netbyte
2022-08-27 02:26:00 +08:00
commit 3f6246d5bb
11 changed files with 309 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
.vscode
.idea
+5
View File
@@ -0,0 +1,5 @@
language: go
go:
- 1.18
script:
- go test -v ./...
Executable
+20
View File
@@ -0,0 +1,20 @@
The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+14
View File
@@ -0,0 +1,14 @@
# go-gateway
A simple library for discovering the IP address of the default gateway.
[![Build Status](https://travis-ci.org/net-byte/go-gateway.svg)](https://travis-ci.org/net-byte/go-gateway)
Provides implementations for:
+ Linux
+ MacOS
+ Windows
Other platforms use an implementation that always returns an error.
+23
View File
@@ -0,0 +1,23 @@
package 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)
)
// DiscoverGatewayIPv4 is the OS independent function to get the default ipv4 gateway
func DiscoverGatewayIPv4() (ip net.IP, err error) {
return discoverGatewayOSSpecificIPv4()
}
// DiscoverGatewayIPv6 is the OS independent function to get the default ipv6 gateway
func DiscoverGatewayIPv6() (ip net.IP, err error) {
return discoverGatewayOSSpecificIPv6()
}
+27
View File
@@ -0,0 +1,27 @@
//go:build darwin
// +build darwin
package gateway
import (
"net"
"os/exec"
)
func discoverGatewayOSSpecificIPv4() (ip net.IP, err error) {
ipstr := execCmd("sh", "-c", "route -n get default | grep 'gateway' | awk 'NR==1{print $2}'")
ipv4 := net.ParseIP(ipstr)
if ipv4 == nil {
return nil, errCantParse
}
return ipv4, nil
}
func discoverGatewayOSSpecificIPv6() (ip net.IP, err error) {
ipstr := execCmd("sh", "-c", "route -6 -n get default | grep 'gateway' | awk 'NR==1{print $2}'")
ipv6 := net.ParseIP(ipstr)
if ipv6 == nil {
return nil, errCantParse
}
return ipv6, nil
}
+26
View File
@@ -0,0 +1,26 @@
//go:build linux
// +build linux
package gateway
import (
"net"
)
func discoverGatewayOSSpecificIPv4() (ip net.IP, err error) {
ipstr := execCmd("sh", "-c", "route -n | grep 'UG[ \t]' | awk 'NR==1{print $2}'")
ipv4 := net.ParseIP(ipstr)
if ipv4 == nil {
return nil, errCantParse
}
return ipv4, nil
}
func discoverGatewayOSSpecificIPv6() (ip net.IP, err error) {
ipstr := execCmd("sh", "-c", "route -6 -n | grep 'UG[ \t]' | awk 'NR==1{print $2}'")
ipv6 := net.ParseIP(ipstr)
if ipv6 == nil {
return nil, errCantParse
}
return ipv6, nil
}
+141
View File
@@ -0,0 +1,141 @@
package gateway
import (
"log"
"net"
"os/exec"
"strings"
)
type windowsRouteStructIPv4 struct {
Destination string
Netmask string
Gateway string
Interface string
Metric string
}
type windowsRouteStructIPv6 struct {
If string
Metric string
Destination string
Gateway string
}
func parseToWindowsRouteStructIPv4(output []byte) (windowsRouteStructIPv4, 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 windowsRouteStructIPv4{}, errNoGateway
}
fields := strings.Fields(lines[idx+2])
if len(fields) < 5 {
return windowsRouteStructIPv4{}, errCantParse
}
return windowsRouteStructIPv4{
Destination: fields[0],
Netmask: fields[1],
Gateway: fields[2],
Interface: fields[3],
Metric: fields[4],
}, nil
}
if strings.HasPrefix(line, "=======") {
sep++
continue
}
}
return windowsRouteStructIPv4{}, errNoGateway
}
func parseToWindowsRouteStructIPv6(output []byte) (windowsRouteStructIPv6, error) {
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 windowsRouteStructIPv6{}, errNoGateway
}
fields := strings.Fields(lines[idx+2])
if len(fields) < 5 {
return windowsRouteStructIPv6{}, errCantParse
}
return windowsRouteStructIPv6{
If: fields[0],
Metric: fields[1],
Destination: fields[2],
Gateway: fields[3],
}, nil
}
if strings.HasPrefix(line, "=======") {
sep++
continue
}
}
return windowsRouteStructIPv6{}, errNoGateway
}
func parseWindowsGatewayIPv4(output []byte) (net.IP, error) {
parsedOutput, err := parseToWindowsRouteStructIPv4(output)
if err != nil {
return nil, err
}
ip := net.ParseIP(parsedOutput.Gateway)
if ip == nil {
return nil, errCantParse
}
return ip, nil
}
func parseWindowsGatewayIPv6(output []byte) (net.IP, error) {
parsedOutput, err := parseToWindowsRouteStructIPv6(output)
if err != nil {
return nil, err
}
ip := net.ParseIP(parsedOutput.Gateway)
if ip == nil {
return nil, errCantParse
}
return ip, nil
}
func execCmd(c string, args ...string) string {
cmd := exec.Command(c, args...)
out, err := cmd.Output()
if err != nil {
log.Println("failed to exec cmd:", err)
}
if len(out) == 0 {
return ""
}
s := string(out)
return strings.ReplaceAll(s, "\n", "")
}
+16
View File
@@ -0,0 +1,16 @@
//go:build !darwin && !linux && !windows && !solaris && !freebsd
// +build !darwin,!linux,!windows,!solaris,!freebsd
package gateway
import (
"net"
)
func discoverGatewayOSSpecificIPv4() (ip net.IP, err error) {
return ip, errNotImplemented
}
func discoverGatewayOSSpecificIPv6() (ip net.IP, err error) {
return ip, errNotImplemented
}
+32
View File
@@ -0,0 +1,32 @@
//go:build windows
// +build windows
package gateway
import (
"net"
"os/exec"
"syscall"
)
func discoverGatewayOSSpecificIPv4() (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 parseWindowsGatewayIPv4(output)
}
func discoverGatewayOSSpecificIPv6() (ip net.IP, err error) {
routeCmd := exec.Command("route", "print","-6" "::/0")
routeCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
output, err := routeCmd.CombinedOutput()
if err != nil {
return nil, err
}
return parseWindowsGatewayIPv6(output)
}
+3
View File
@@ -0,0 +1,3 @@
module github.com/net-byte/go-gateway
go 1.18