This repository has been archived by the owner on Oct 24, 2022. It is now read-only.
/
iputil.go
220 lines (191 loc) · 4.75 KB
/
iputil.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package iputil
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"strconv"
"strings"
"github.com/projectdiscovery/stringsutil"
"go.uber.org/multierr"
)
// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
func IsIP(str string) bool {
return net.ParseIP(str) != nil
}
// IsPort checks if a string represents a valid port
func IsPort(str string) bool {
if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {
return true
}
return false
}
// IsIPv4 checks if the string is an IP version 4.
func IsIPv4(ips ...interface{}) bool {
for _, ip := range ips {
switch ipv := ip.(type) {
case string:
parsedIP := net.ParseIP(ipv)
isIP4 := parsedIP != nil && parsedIP.To4() != nil && stringsutil.ContainsAny(ipv, ".")
if !isIP4 {
return false
}
case net.IP:
isIP4 := ipv != nil && ipv.To4() != nil && stringsutil.ContainsAny(ipv.String(), ".")
if !isIP4 {
return false
}
}
}
return true
}
// IsIPv6 checks if the string is an IP version 6.
func IsIPv6(ips ...interface{}) bool {
for _, ip := range ips {
switch ipv := ip.(type) {
case string:
parsedIP := net.ParseIP(ipv)
isIP6 := parsedIP != nil && parsedIP.To16() != nil && stringsutil.ContainsAny(ipv, ":")
if !isIP6 {
return false
}
case net.IP:
isIP6 := ipv != nil && ipv.To16() != nil && stringsutil.ContainsAny(ipv.String(), ":")
if !isIP6 {
return false
}
}
}
return true
}
// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6)
func IsCIDR(str string) bool {
_, _, err := net.ParseCIDR(str)
return err == nil
}
// IsCIDR checks if the string is an valid CIDR after replacing - with /
func IsCidrWithExpansion(str string) bool {
str = strings.ReplaceAll(str, "-", "/")
return IsCIDR(str)
}
// ToCidr converts a cidr string to net.IPNet pointer
func ToCidr(item string) *net.IPNet {
if IsIPv4(item) {
item += "/32"
} else if IsIPv6(item) {
item += "/128"
}
if IsCIDR(item) {
_, ipnet, _ := net.ParseCIDR(item)
// a few ip4 might be expressed as ip6, therefore perform a double conversion
_, ipnet, _ = net.ParseCIDR(ipnet.String())
return ipnet
}
return nil
}
// AsIPV4CIDR converts ipv4 cidr to net.IPNet pointer
func AsIPV4IpNet(IPV4 string) *net.IPNet {
if IsIPv4(IPV4) {
IPV4 += "/32"
}
_, network, err := net.ParseCIDR(IPV4)
if err != nil {
return nil
}
return network
}
// AsIPV6IpNet converts ipv6 cidr to net.IPNet pointer
func AsIPV6IpNet(IPV6 string) *net.IPNet {
if IsIPv6(IPV6) {
IPV6 += "/64"
}
_, network, err := net.ParseCIDR(IPV6)
if err != nil {
return nil
}
return network
}
// AsIPV4CIDR converts ipv4 ip to cidr string
func AsIPV4CIDR(IPV4 string) string {
if IsIP(IPV4) {
return IPV4 + "/32"
}
return IPV4
}
// AsIPV4CIDR converts ipv6 ip to cidr string
func AsIPV6CIDR(IPV6 string) string {
// todo
return IPV6
}
// WhatsMyIP attempts to obtain the external ip through public api
// Copied from https://github.com/projectdiscovery/naabu/blob/master/v2/pkg/scan/externalip.go
func WhatsMyIP() (string, error) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://api.ipify.org?format=text", nil)
if err != nil {
return "", nil
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("error fetching ip: %s", resp.Status)
}
ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(ip), nil
}
// GetSourceIP gets the local ip based the destination ip
func GetSourceIP(target string) (net.IP, error) {
hostPort := net.JoinHostPort(target, "12345")
serverAddr, err := net.ResolveUDPAddr("udp", hostPort)
if err != nil {
return nil, err
}
con, dialUpErr := net.DialUDP("udp", nil, serverAddr)
if dialUpErr != nil {
return nil, dialUpErr
}
defer con.Close()
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, nil
}
return nil, errors.New("could not get source ip")
}
// GetBindableAddress on port p from a list of ips
func GetBindableAddress(port int, ips ...string) (string, error) {
var errs error
// iterate over ips and return the first bindable one on port p
for _, ip := range ips {
if ip == "" {
continue
}
ipPort := net.JoinHostPort(ip, fmt.Sprint(port))
// check if we can listen on tcp
l, err := net.Listen("tcp", ipPort)
if err != nil {
errs = multierr.Append(errs, err)
continue
}
l.Close()
udpAddr := net.UDPAddr{
Port: port,
IP: net.ParseIP(ip),
}
// check if we can listen on udp
lu, err := net.ListenUDP("udp", &udpAddr)
if err != nil {
errs = multierr.Append(errs, err)
continue
}
lu.Close()
// we found a bindable ip
return ip, nil
}
return "", errs
}