project init
This commit is contained in:
commit
1cf977be25
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
### Go template
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
|
.glide/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
vendor/
|
||||||
|
glide.lock
|
||||||
|
.DS_Store
|
||||||
|
dist/
|
||||||
|
debug
|
32
.vscode/launch.json
vendored
Normal file
32
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"remotePath": "",
|
||||||
|
"port": 2345,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"program": "${workspaceRoot}/main.go",
|
||||||
|
"env": {},
|
||||||
|
"args": [],
|
||||||
|
"showLog": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "File Debug",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"remotePath": "",
|
||||||
|
"port": 2345,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"program": "${fileDirname}",
|
||||||
|
"env": {},
|
||||||
|
"args": [],
|
||||||
|
"showLog": true
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
// Specifies Lint tool name.
|
||||||
|
"go.lintTool": "gometalinter",
|
||||||
|
|
||||||
|
// Flags to pass to Lint tool (e.g. ["-min_confidence=.8"])
|
||||||
|
"go.lintFlags": [
|
||||||
|
"--config=${workspaceRoot}/golint.json"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
13
channel/action.go
Normal file
13
channel/action.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package channel
|
||||||
|
|
||||||
|
type ActionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ActionTypeCreate ActionType = iota
|
||||||
|
ActionTypeDelete
|
||||||
|
ActionTypeUpdate
|
||||||
|
)
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Type ActionType
|
||||||
|
}
|
111
context/context.go
Normal file
111
context/context.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextKey string
|
||||||
|
|
||||||
|
func (c ContextKey) String() string {
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContext(parent Context) Context {
|
||||||
|
c := &defaultContext{
|
||||||
|
Context: parent,
|
||||||
|
}
|
||||||
|
c.attributes = make(map[interface{}]interface{})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type Context interface {
|
||||||
|
Parent() Context
|
||||||
|
SetAttribute(key interface{}, value interface{})
|
||||||
|
GetAttribute(key interface{}) (value interface{})
|
||||||
|
RemoveAttribute(key interface{})
|
||||||
|
ContainsAttribute(key interface{}) (exist bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultContext struct {
|
||||||
|
Context
|
||||||
|
attributes map[interface{}]interface{}
|
||||||
|
|
||||||
|
mtx sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) Parent() Context {
|
||||||
|
return dc.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) SetAttribute(key interface{}, value interface{}) {
|
||||||
|
dc.checkInitialized()
|
||||||
|
|
||||||
|
if key == nil {
|
||||||
|
panic("nil key")
|
||||||
|
}
|
||||||
|
if !reflect.TypeOf(key).Comparable() {
|
||||||
|
panic("key is not comparable")
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.mtx.Lock()
|
||||||
|
defer dc.mtx.Unlock()
|
||||||
|
|
||||||
|
dc.attributes[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) GetAttribute(key interface{}) (value interface{}) {
|
||||||
|
dc.checkInitialized()
|
||||||
|
|
||||||
|
dc.mtx.RLock()
|
||||||
|
defer dc.mtx.RUnlock()
|
||||||
|
|
||||||
|
if _, ok := dc.attributes[key]; ok {
|
||||||
|
return dc.attributes[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil == dc.Context {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return dc.Context.GetAttribute(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) RemoveAttribute(key interface{}) {
|
||||||
|
dc.checkInitialized()
|
||||||
|
|
||||||
|
dc.mtx.Lock()
|
||||||
|
defer dc.mtx.Unlock()
|
||||||
|
|
||||||
|
if _, ok := dc.attributes[key]; ok {
|
||||||
|
delete(dc.attributes, key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil == dc.Context {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.Context.RemoveAttribute(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) ContainsAttribute(key interface{}) (exist bool) {
|
||||||
|
dc.checkInitialized()
|
||||||
|
|
||||||
|
dc.mtx.RLock()
|
||||||
|
defer dc.mtx.RUnlock()
|
||||||
|
|
||||||
|
if _, ok := dc.attributes[key]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil == dc.Context {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return dc.Context.ContainsAttribute(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *defaultContext) checkInitialized() {
|
||||||
|
if nil == dc.attributes {
|
||||||
|
panic("Attribute Manager: must be initialized")
|
||||||
|
}
|
||||||
|
}
|
50
encoding/json/json.go
Normal file
50
encoding/json/json.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
cur "git.loafle.net/commons/util-go/reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetValueWithJSONStringArray set the value of json string array
|
||||||
|
// raw([]byte) is ["1", {"a": 1}, [1, 2]]
|
||||||
|
// targets([]interface{}) is array of pointer ex) *int, *string, *[], *map, *struct
|
||||||
|
func SetValueWithJSONStringArray(raw []byte, targets []interface{}) error {
|
||||||
|
var values []string
|
||||||
|
if err := json.Unmarshal(raw, &values); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(targets) != len(values) {
|
||||||
|
return fmt.Errorf("Count of raw[%d] and targets[%d] is not same", len(values), len(targets))
|
||||||
|
}
|
||||||
|
|
||||||
|
for indexI := 0; indexI < len(values); indexI++ {
|
||||||
|
target := targets[indexI]
|
||||||
|
value := values[indexI]
|
||||||
|
|
||||||
|
if reflect.Ptr != reflect.TypeOf(target).Kind() {
|
||||||
|
return fmt.Errorf("Type of target[%d] must be ptr but is %s, value=%s", indexI, reflect.TypeOf(target).Kind(), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch reflect.TypeOf(target).Elem().Kind() {
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct:
|
||||||
|
if err := json.Unmarshal([]byte(value), &target); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
return fmt.Errorf("Type of target[%d] cannot be double ptr, value=%s", indexI, value)
|
||||||
|
default:
|
||||||
|
cv, err := cur.ConvertToType(value, reflect.TypeOf(target).Elem())
|
||||||
|
if nil != err {
|
||||||
|
return fmt.Errorf("Type conversion of value[%s] has been failed to %s[%d]", value, reflect.TypeOf(target).Elem().Kind(), indexI)
|
||||||
|
}
|
||||||
|
|
||||||
|
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(cv))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
14
encoding/json/number.go
Normal file
14
encoding/json/number.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NumberToInt(n json.Number) (int, error) {
|
||||||
|
n64, err := strconv.ParseInt(n.String(), 10, 32)
|
||||||
|
if nil != err {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(n64), nil
|
||||||
|
}
|
2
glide.yaml
Normal file
2
glide.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
package: git.loafle.net/commons/util-go
|
||||||
|
import: []
|
128
net/cidr/range-v4.go
Normal file
128
net/cidr/range-v4.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package cidr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.loafle.net/commons/util-go/net/converter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cidrRangeIPv4 struct {
|
||||||
|
cidrNet *net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Contains(ip net.IP) bool {
|
||||||
|
return cr.cidrNet.Contains(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) First() net.IP {
|
||||||
|
nIP := cr.Network()
|
||||||
|
return cr.Next(nIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Last() net.IP {
|
||||||
|
bIP := cr.Broadcast()
|
||||||
|
return cr.Previous(bIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Range() []net.IP {
|
||||||
|
fIP := cr.First()
|
||||||
|
if nil == fIP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lIP := cr.Last()
|
||||||
|
if nil == lIP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fNum := converter.IPv4ToInt(fIP.To4())
|
||||||
|
lNum := converter.IPv4ToInt(lIP.To4())
|
||||||
|
|
||||||
|
r := make([]net.IP, 0)
|
||||||
|
for i := fNum; i <= lNum; i++ {
|
||||||
|
r = append(r, converter.IntToIPv4(i))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// (!Contains(startIP) || !Contains(endIP)) return nil
|
||||||
|
// (startIP > endIP) return nil
|
||||||
|
// (nil != startIP && nil != endIP) return (startIP ~ endIP) + include - exclude
|
||||||
|
// (nil == startIP || nil == endIP) return include - exclude
|
||||||
|
func (cr *cidrRangeIPv4) Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error) {
|
||||||
|
|
||||||
|
res := make(map[int32]bool)
|
||||||
|
|
||||||
|
if nil != startIP && nil != endIP {
|
||||||
|
if !cr.Contains(startIP) {
|
||||||
|
return nil, fmt.Errorf("CIDR Range: CIDR not contains start ip[%v]", startIP)
|
||||||
|
}
|
||||||
|
if !cr.Contains(endIP) {
|
||||||
|
return nil, fmt.Errorf("CIDR Range: CIDR not contains end ip[%v]", endIP)
|
||||||
|
}
|
||||||
|
sNum := converter.IPv4ToInt(startIP.To4())
|
||||||
|
eNum := converter.IPv4ToInt(endIP.To4())
|
||||||
|
if sNum > eNum {
|
||||||
|
return nil, fmt.Errorf("CIDR Range: Start IP[%v] must smaller then End IP[%v]", startIP, endIP)
|
||||||
|
}
|
||||||
|
for i := sNum; i <= eNum; i++ {
|
||||||
|
res[i] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != include {
|
||||||
|
for _, in := range include {
|
||||||
|
iNum := converter.IPv4ToInt(in.To4())
|
||||||
|
if _, ok := res[iNum]; !ok {
|
||||||
|
res[iNum] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != exclude {
|
||||||
|
for _, ex := range exclude {
|
||||||
|
iNum := converter.IPv4ToInt(ex.To4())
|
||||||
|
if _, ok := res[iNum]; ok {
|
||||||
|
delete(res, iNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := make([]net.IP, 0)
|
||||||
|
for k, _ := range res {
|
||||||
|
r = append(r, converter.IntToIPv4(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Broadcast() net.IP {
|
||||||
|
ip := cr.cidrNet.IP.To4()
|
||||||
|
bIP := net.IPv4(0, 0, 0, 0).To4()
|
||||||
|
for i := 0; i < len(bIP); i++ {
|
||||||
|
bIP[i] = ip[i] | ^cr.cidrNet.Mask[i]
|
||||||
|
}
|
||||||
|
return bIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Network() net.IP {
|
||||||
|
ip := cr.cidrNet.IP.To4()
|
||||||
|
return ip.Mask(cr.cidrNet.Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Next(ip net.IP) net.IP {
|
||||||
|
nNum := converter.IPv4ToInt(ip.To4()) + 1
|
||||||
|
nIP := converter.IntToIPv4(nNum)
|
||||||
|
if cr.Contains(nIP) {
|
||||||
|
return nIP
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv4) Previous(ip net.IP) net.IP {
|
||||||
|
nNum := converter.IPv4ToInt(ip.To4()) - 1
|
||||||
|
nIP := converter.IntToIPv4(nNum)
|
||||||
|
if cr.Contains(nIP) {
|
||||||
|
return nIP
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
43
net/cidr/range-v6.go
Normal file
43
net/cidr/range-v6.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package cidr
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type cidrRangeIPv6 struct {
|
||||||
|
cidrNet *net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Contains(ip net.IP) bool {
|
||||||
|
return cr.cidrNet.Contains(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) First() net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Last() net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Range() []net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Broadcast() net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Network() net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Next(ip net.IP) net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cidrRangeIPv6) Previous(ip net.IP) net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
44
net/cidr/range.go
Normal file
44
net/cidr/range.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package cidr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCIDRRanger(cidr string) (CIDRRanger, error) {
|
||||||
|
_, nIPNet, err := net.ParseCIDR(cidr)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(nIPNet.IP) {
|
||||||
|
case net.IPv4len:
|
||||||
|
cr := &cidrRangeIPv4{
|
||||||
|
cidrNet: nIPNet,
|
||||||
|
}
|
||||||
|
return cr, nil
|
||||||
|
case net.IPv6len:
|
||||||
|
cr := &cidrRangeIPv6{
|
||||||
|
cidrNet: nIPNet,
|
||||||
|
}
|
||||||
|
return cr, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Net: not supported IP length")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CIDRRanger interface {
|
||||||
|
Contains(ip net.IP) bool
|
||||||
|
First() net.IP
|
||||||
|
Last() net.IP
|
||||||
|
Range() []net.IP
|
||||||
|
// (!Contains(startIP) || !Contains(endIP)) return error
|
||||||
|
// (startIP > endIP) return error
|
||||||
|
// (nil != startIP && nil != endIP) return (startIP ~ endIP) + include - exclude
|
||||||
|
// (nil == startIP || nil == endIP) return include - exclude
|
||||||
|
Ranges(startIP net.IP, endIP net.IP, include []net.IP, exclude []net.IP) ([]net.IP, error)
|
||||||
|
Broadcast() net.IP
|
||||||
|
Network() net.IP
|
||||||
|
Next(ip net.IP) net.IP
|
||||||
|
Previous(ip net.IP) net.IP
|
||||||
|
}
|
16
net/converter/ip-v4.go
Normal file
16
net/converter/ip-v4.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IPv4ToInt(ip net.IP) int32 {
|
||||||
|
return int32(binary.BigEndian.Uint32(ip.To4()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func IntToIPv4(n int32) net.IP {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(b, uint32(n))
|
||||||
|
return net.IP(b)
|
||||||
|
}
|
15
net/converter/ip-v6.go
Normal file
15
net/converter/ip-v6.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
func IPv6ToInt(ip net.IP) int32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
func IntToIPv6(n int32) net.IP {
|
||||||
|
return nil
|
||||||
|
}
|
17
net/converter/ip.go
Normal file
17
net/converter/ip.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IPToInt(ip net.IP) (int32, error) {
|
||||||
|
switch len(ip) {
|
||||||
|
case net.IPv4len:
|
||||||
|
return IPv4ToInt(ip), nil
|
||||||
|
case net.IPv6len:
|
||||||
|
return IPv6ToInt(ip), nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("Net: not supported IP length")
|
||||||
|
}
|
||||||
|
}
|
18
net/converter/mac.go
Normal file
18
net/converter/mac.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
macStripRegexp = regexp.MustCompile(`[^a-fA-F0-9]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func MacToUint64(hwAddr net.HardwareAddr) (uint64, error) {
|
||||||
|
mac := hwAddr.String()
|
||||||
|
hex := macStripRegexp.ReplaceAllLiteralString(mac, "")
|
||||||
|
|
||||||
|
return strconv.ParseUint(hex, 16, 64)
|
||||||
|
}
|
162
net/gateway/gateway-common.go
Normal file
162
net/gateway/gateway-common.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNoGateway = errors.New("no gateway found")
|
||||||
|
|
||||||
|
func parseWindowsRoutePrint(output []byte) (net.IP, string, error) {
|
||||||
|
// Windows route output format is always like this:
|
||||||
|
// ===========================================================================
|
||||||
|
// 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
|
||||||
|
// ===========================================================================
|
||||||
|
// I'm trying to pick the active route,
|
||||||
|
// then jump 2 lines and pick the third IP
|
||||||
|
// Not using regex because output is quite standard from Windows XP to 8 (NEEDS TESTING)
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for idx, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "Active Routes:") {
|
||||||
|
if len(lines) <= idx+2 {
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(lines[idx+2])
|
||||||
|
if len(fields) < 3 {
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(fields[2])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinuxIPRoute(output []byte) (net.IP, string, error) {
|
||||||
|
// Linux '/usr/bin/ip route show' format looks like this:
|
||||||
|
// default via 192.168.178.1 dev wlp3s0 metric 303
|
||||||
|
// 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 3 && fields[0] == "default" {
|
||||||
|
ip := net.ParseIP(fields[2])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[4], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinuxRoute(output []byte) (net.IP, string, error) {
|
||||||
|
// Linux route out format is always like this:
|
||||||
|
// Kernel IP routing table
|
||||||
|
// Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
// 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 && fields[0] == "0.0.0.0" {
|
||||||
|
ip := net.ParseIP(fields[1])
|
||||||
|
if ip != nil {
|
||||||
|
return ip, fields[7], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDarwinRouteGet(output []byte) (net.IP, string, error) {
|
||||||
|
// Darwin route out format is always like this:
|
||||||
|
// route to: default
|
||||||
|
// destination: default
|
||||||
|
// mask: default
|
||||||
|
// gateway: 192.168.1.1
|
||||||
|
// interface: tun0
|
||||||
|
// flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Darwin route out format is always like this:
|
||||||
|
// Internet:
|
||||||
|
// Destination Gateway Flags Refs Use Netif Expire
|
||||||
|
// default 192.168.10.254 UGSc 194 0 en3
|
||||||
|
// 127 127.0.0.1 UCS 0 429 lo0
|
||||||
|
// 127.0.0.1 127.0.0.1 UH 1 587632 lo0
|
||||||
|
// 169.254 link#7 UCS 0 0 en3
|
||||||
|
// 192.168.10 link#7 UCS 4 0 en3
|
||||||
|
// 192.168.10.1 0:11:32:7f:20:61 UHLWIi 1 202 en3 1065
|
||||||
|
// 224.0.0/4 link#7 UmCS 3 0 en3
|
||||||
|
// 224.0.0.251 1:0:5e:0:0:fb UHmLWI 0 2325 en3
|
||||||
|
// 239.192.152.143 1:0:5e:40:98:8f UHmLWI 0 22892 en3
|
||||||
|
// 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI 0 15988 en3
|
||||||
|
// 255.255.255.255/32 link#7 UCS 0 0 en3
|
||||||
|
|
||||||
|
// Internet6:
|
||||||
|
// Destination Gateway Flags Netif Expire
|
||||||
|
// default fe80::%utun0 UGcI utun0
|
||||||
|
// default fe80::%utun1 UGcI utun1
|
||||||
|
// default fe80::%utun2 UGcI utun2
|
||||||
|
// default fe80::%utun3 UGcI utun3
|
||||||
|
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, fields[5], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBSDSolarisNetstat(output []byte) (net.IP, string, 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, fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", errNoGateway
|
||||||
|
}
|
16
net/gateway/gateway-darwin.go
Normal file
16
net/gateway/gateway-darwin.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseDarwinRouteGet(output)
|
||||||
|
}
|
16
net/gateway/gateway-freebsd.go
Normal file
16
net/gateway/gateway-freebsd.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseBSDSolarisNetstat(output)
|
||||||
|
}
|
34
net/gateway/gateway-linux.go
Normal file
34
net/gateway/gateway-linux.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
ip, iface, err = discoverGatewayUsingRoute()
|
||||||
|
if err != nil {
|
||||||
|
ip, iface, err = discoverGatewayUsingIp()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverGatewayUsingIp() (net.IP, string, error) {
|
||||||
|
routeCmd := exec.Command("ip", "route", "show")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLinuxIPRoute(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverGatewayUsingRoute() (net.IP, string, error) {
|
||||||
|
routeCmd := exec.Command("route", "-n")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseLinuxRoute(output)
|
||||||
|
}
|
16
net/gateway/gateway-solaris.go
Normal file
16
net/gateway/gateway-solaris.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("netstat", "-rn")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseBSDSolarisNetstat(output)
|
||||||
|
}
|
248
net/gateway/gateway-test.go
Normal file
248
net/gateway/gateway-test.go
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testcase struct {
|
||||||
|
output []byte
|
||||||
|
ok bool
|
||||||
|
gateway string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseWindowsRoutePrint(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
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:
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
`)
|
||||||
|
badRoute1 := []byte(`
|
||||||
|
IPv4 Route Table
|
||||||
|
===========================================================================
|
||||||
|
Active Routes:
|
||||||
|
===========================================================================
|
||||||
|
Persistent Routes:
|
||||||
|
`)
|
||||||
|
badRoute2 := []byte(`
|
||||||
|
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"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute1, false, ""},
|
||||||
|
{badRoute2, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseWindowsRoutePrint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLinuxIPRoutePrint(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
default via 192.168.178.1 dev wlp3s0 metric 303
|
||||||
|
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
default via foo dev wlp3s0 metric 303
|
||||||
|
192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "192.168.178.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseLinuxIPRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLinuxRoutePrint(t *testing.T) {
|
||||||
|
correctData := []byte(`
|
||||||
|
Kernel IP routing table
|
||||||
|
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
`)
|
||||||
|
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(`
|
||||||
|
Kernel IP routing table
|
||||||
|
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
`)
|
||||||
|
badRoute := []byte(`
|
||||||
|
Kernel IP routing table
|
||||||
|
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||||
|
0.0.0.0 foo 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
`)
|
||||||
|
|
||||||
|
testcases := []testcase{
|
||||||
|
{correctData, true, "192.168.1.1"},
|
||||||
|
{randomData, false, ""},
|
||||||
|
{noRoute, false, ""},
|
||||||
|
{badRoute, false, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
test(t, testcases, parseLinuxRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
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: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
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: <UP,GATEWAY,DONE,STATIC,PRCLONING>
|
||||||
|
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 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 test(t *testing.T, testcases []testcase, fn func([]byte) (net.IP, string, error)) {
|
||||||
|
for i, tc := range testcases {
|
||||||
|
net, iface, 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)
|
||||||
|
}
|
||||||
|
if "" == iface {
|
||||||
|
t.Errorf("Unexpected interface")
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
t.Errorf("Unexpected nil error in test #%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
net/gateway/gateway-unimplemented.go
Normal file
14
net/gateway/gateway-unimplemented.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build !darwin,!linux,!windows,!solaris,!freebsd
|
||||||
|
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
err = fmt.Errorf("DiscoverGateway not implemented for OS %s", runtime.GOOS)
|
||||||
|
return
|
||||||
|
}
|
16
net/gateway/gateway-windows.go
Normal file
16
net/gateway/gateway-windows.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscoverGateway() (ip net.IP, iface string, err error) {
|
||||||
|
routeCmd := exec.Command("route", "print", "0.0.0.0")
|
||||||
|
output, err := routeCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseWindowsRoutePrint(output)
|
||||||
|
}
|
20
net/url/url.go
Normal file
20
net/url/url.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package url
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Join is concat URL string and path
|
||||||
|
// ex) http://127.0.0.1/ and /entry
|
||||||
|
func Join(u string, p string) (string, error) {
|
||||||
|
var err error
|
||||||
|
var rURL *url.URL
|
||||||
|
|
||||||
|
if rURL, err = url.Parse(u); nil != err {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
rURL.Path = path.Join(rURL.Path, p)
|
||||||
|
return rURL.String(), nil
|
||||||
|
}
|
1153
reflect/convert.go
Normal file
1153
reflect/convert.go
Normal file
File diff suppressed because it is too large
Load Diff
41
reflect/error.go
Normal file
41
reflect/error.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package reflect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrCanNotConvertType struct {
|
||||||
|
value interface{}
|
||||||
|
from reflect.Type
|
||||||
|
to reflect.Type
|
||||||
|
reason []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrCanNotConvertType) Error() string {
|
||||||
|
var (
|
||||||
|
reason = strings.Join(e.reason, ", ")
|
||||||
|
)
|
||||||
|
|
||||||
|
if reason != "" {
|
||||||
|
reason = ", reason: " + reason
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Can not convert '%#v' of type '%s' to '%s'%s",
|
||||||
|
e.value,
|
||||||
|
e.from,
|
||||||
|
e.to,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrCanNotConvertType(value interface{}, from reflect.Type, to reflect.Type, reason ...string) ErrCanNotConvertType {
|
||||||
|
return ErrCanNotConvertType{
|
||||||
|
value: value,
|
||||||
|
from: from,
|
||||||
|
to: to,
|
||||||
|
reason: reason,
|
||||||
|
}
|
||||||
|
}
|
31
reflect/indirect.go
Normal file
31
reflect/indirect.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package reflect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Indirect(v interface{}) interface{} {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return IndirectValue(reflect.ValueOf(v)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndirectValue(reflectValue reflect.Value) reflect.Value {
|
||||||
|
if reflectValue.Kind() == reflect.Ptr {
|
||||||
|
return reflectValue.Elem()
|
||||||
|
}
|
||||||
|
return reflectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndirectType(reflectType reflect.Type) reflect.Type {
|
||||||
|
if reflectType == TypeInvalid {
|
||||||
|
return TypeInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflectType.Kind() == reflect.Ptr {
|
||||||
|
return reflectType.Elem()
|
||||||
|
}
|
||||||
|
return reflectType
|
||||||
|
}
|
35
reflect/kind.go
Normal file
35
reflect/kind.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package reflect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Invalid = reflect.Invalid
|
||||||
|
Bool = reflect.Bool
|
||||||
|
Int = reflect.Int
|
||||||
|
Int8 = reflect.Int8
|
||||||
|
Int16 = reflect.Int16
|
||||||
|
Int32 = reflect.Int32
|
||||||
|
Int64 = reflect.Int64
|
||||||
|
Uint = reflect.Uint
|
||||||
|
Uint8 = reflect.Uint8
|
||||||
|
Uint16 = reflect.Uint16
|
||||||
|
Uint32 = reflect.Uint32
|
||||||
|
Uint64 = reflect.Uint64
|
||||||
|
Uintptr = reflect.Uintptr
|
||||||
|
Float32 = reflect.Float32
|
||||||
|
Float64 = reflect.Float64
|
||||||
|
Complex64 = reflect.Complex64
|
||||||
|
Complex128 = reflect.Complex128
|
||||||
|
Array = reflect.Array
|
||||||
|
Chan = reflect.Chan
|
||||||
|
Func = reflect.Func
|
||||||
|
Interface = reflect.Interface
|
||||||
|
Map = reflect.Map
|
||||||
|
Ptr = reflect.Ptr
|
||||||
|
Slice = reflect.Slice
|
||||||
|
String = reflect.String
|
||||||
|
Struct = reflect.Struct
|
||||||
|
UnsafePointer = reflect.UnsafePointer
|
||||||
|
)
|
72
reflect/type.go
Normal file
72
reflect/type.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package reflect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TypeOf = reflect.TypeOf
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Types = []reflect.Type{
|
||||||
|
TypeBool,
|
||||||
|
TypeInt,
|
||||||
|
TypeInt8,
|
||||||
|
TypeInt16,
|
||||||
|
TypeInt32,
|
||||||
|
TypeInt64,
|
||||||
|
TypeUint,
|
||||||
|
TypeUint8,
|
||||||
|
TypeUint16,
|
||||||
|
TypeUint32,
|
||||||
|
TypeUint64,
|
||||||
|
TypeFloat32,
|
||||||
|
TypeFloat64,
|
||||||
|
TypeComplex64,
|
||||||
|
TypeComplex128,
|
||||||
|
TypeUintptr,
|
||||||
|
TypeString,
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInvalid = reflect.Type(nil)
|
||||||
|
TypeBool = reflect.TypeOf(false)
|
||||||
|
TypeInt = reflect.TypeOf(int(0))
|
||||||
|
TypeInt8 = reflect.TypeOf(int8(0))
|
||||||
|
TypeInt16 = reflect.TypeOf(int16(0))
|
||||||
|
TypeInt32 = reflect.TypeOf(int32(0))
|
||||||
|
TypeInt64 = reflect.TypeOf(int64(0))
|
||||||
|
TypeUint = reflect.TypeOf(uint(0))
|
||||||
|
TypeUint8 = reflect.TypeOf(uint8(0))
|
||||||
|
TypeUint16 = reflect.TypeOf(uint16(0))
|
||||||
|
TypeUint32 = reflect.TypeOf(uint32(0))
|
||||||
|
TypeUint64 = reflect.TypeOf(uint64(0))
|
||||||
|
TypeFloat32 = reflect.TypeOf(float32(0))
|
||||||
|
TypeFloat64 = reflect.TypeOf(float64(0))
|
||||||
|
TypeComplex64 = reflect.TypeOf(complex64(0))
|
||||||
|
TypeComplex128 = reflect.TypeOf(complex128(0))
|
||||||
|
TypeUintptr = reflect.TypeOf(uintptr(0))
|
||||||
|
TypeString = reflect.TypeOf(string(""))
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsTypeKind(t reflect.Type, kind reflect.Kind, removePtr bool) bool {
|
||||||
|
if reflect.Ptr == t.Kind() {
|
||||||
|
if removePtr {
|
||||||
|
return IsTypeKind(t.Elem(), kind, removePtr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return kind == t.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTypeInfo(t reflect.Type) (realType reflect.Type, pkgName string, name string) {
|
||||||
|
if reflect.Ptr == t.Kind() {
|
||||||
|
return GetTypeInfo(t.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, t.PkgPath(), t.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertStringToType() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user