-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpubip.go
126 lines (114 loc) · 2.6 KB
/
pubip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package pubip
import (
"errors"
"math/rand"
"sync"
"time"
)
// Master - services used:
// http://ident.me
// http://ipecho.net
// https://ifconfig.co
// https://ipinfo.io
// https://icanhazip.com
// http://bot.whatismyipaddress.com
// https://myexternalip.com
// http://checkip.amazonaws.com
// https://ifcfg.me
// https://ip.tyk.nu
// https://tnx.nl
// https://l2.io
// https://ip.appspot.com
// https://ipof.in
// https://wgetip.com
// http://eth0.me
// https://tnx.nl
type Master struct {
Parallel int // number of service to try in parallel
Format IPType // accepted ip address return format
}
// IPFn is an alias for a function which returns an ip address.
type IPFn func() (string, error)
// IPFuncs is an alias for a slice of IPFn functions.
type IPFuncs []IPFn
// IPType of the ip address
type IPType int
const (
// IPv6orIPv4 accept both
IPv6orIPv4 IPType = iota
// IPv6 only
IPv6
// IPv4 only
IPv4
)
func (t IPType) String() string {
switch t {
case IPv6orIPv4:
return "IPv6orIPv4"
case IPv6:
return "IPv6"
case IPv4:
return "IPv4"
}
panic("unknown IPType: " + string(t))
}
type url string
// ErrIPUnknown is returned if all services fail to return the public ip address.
var ErrIPUnknown = errors.New("Public ip address unknown")
// NewMaster object. Allows repetitive request to get the public ip address.
func NewMaster() *Master {
rand.Seed(time.Now().Unix())
return &Master{
Parallel: 2,
Format: IPv6orIPv4,
}
}
// Address returns the public IP address, using a random service.
// Services are queried parallel if Master.Parallel is set > 1 (default 2).
// IPv6 or IPv4 can be set with Master.Format (default IPv6orIPv4).
// The maximal timeout for a service-query is 2 second.
func (m *Master) Address() (string, error) {
a := AllFuncs(m.Format) // TODO replace with new fn
if m.Parallel < 1 {
m.Parallel = 1
} else if m.Parallel > len(a) {
m.Parallel = len(a)
}
return m.addressParallel(a)
}
func (m *Master) addressParallel(a IPFuncs) (string, error) {
var wg sync.WaitGroup
inpc := make(chan IPFn, len(a))
resc := make(chan string)
// start worker
for i := 0; i < m.Parallel; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// worker
for fn := range inpc {
if ip, err := fn(); err == nil {
resc <- ip
break
}
}
}()
}
// feed work to worker
go func() {
defer close(inpc)
for _, fn := range a {
inpc <- fn
}
}()
// wait for worker to finish
go func() {
defer close(resc)
wg.Wait()
}()
// return first ip address, if any
for ip := range resc {
return ip, nil
}
return "", ErrIPUnknown
}