Files
Olares/cli/pkg/utils/iputils.go

185 lines
3.8 KiB
Go
Executable File

package utils
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"regexp"
"strings"
"time"
"github.com/beclab/Olares/cli/pkg/core/logger"
)
const (
XForwardedFor = "X-Forwarded-For"
XRealIP = "X-Real-IP"
XClientIP = "x-client-ip"
)
func RemoteIp(req *http.Request) string {
remoteAddr := req.RemoteAddr
if ip := req.Header.Get(XClientIP); ip != "" {
remoteAddr = ip
} else if ip := req.Header.Get(XRealIP); ip != "" {
remoteAddr = ip
} else if ip = req.Header.Get(XForwardedFor); ip != "" {
remoteAddr = ip
} else {
remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
}
if remoteAddr == "::1" {
remoteAddr = "127.0.0.1"
}
return remoteAddr
}
// GetMyExternalIPAddr get my network outgoing ip address
func GetMyExternalIPAddr() string {
sites := map[string]string{
"httpbin": "https://httpbin.org/ip",
"ifconfigme": "https://ifconfig.me/all.json",
"externalip": "https://myexternalip.com/json",
}
type httpBin struct {
Origin string `json:"origin"`
}
type ifconfigMe struct {
IPAddr string `json:"ip_addr"`
RemoteHost string `json:"remote_host,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
Port int `json:"port,omitempty"`
Method string `json:"method,omitempty"`
Encoding string `json:"encoding,omitempty"`
Via string `json:"via,omitempty"`
Forwarded string `json:"forwarded,omitempty"`
}
type externalIP struct {
IP string `json:"ip"`
}
var unmarshalFuncs = map[string]func(v []byte) string{
"httpbin": func(v []byte) string {
var hb httpBin
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
return hb.Origin
}
return ""
},
"ifconfigme": func(v []byte) string {
var ifMe ifconfigMe
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
return ifMe.IPAddr
}
return ""
},
"externalip": func(v []byte) string {
var extip externalIP
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
return extip.IP
}
return ""
},
}
ch := make(chan any, len(sites))
for site := range sites {
go func(name string) {
c := http.Client{Timeout: 5 * time.Second}
resp, err := c.Get(sites[name])
if err != nil {
ch <- err
return
}
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
ch <- err
return
}
ip := unmarshalFuncs[name](respBytes)
//println(name, site, ip)
ch <- ip
}(site)
}
tr := time.NewTimer(time.Duration(5*len(sites)+3) * time.Second)
LOOP:
for i := 0; i < len(sites); i++ {
select {
case r, ok := <-ch:
if !ok {
continue
}
switch v := r.(type) {
case string:
ip := net.ParseIP(v)
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
return v
}
case error:
logger.Warnf("got an error, %v", v)
}
case <-tr.C:
tr.Stop()
logger.Warnf("timed out")
break LOOP
}
}
return ""
}
func ExtractIP(host string) ([]string, error) {
var ips []string
re := regexp.MustCompile(`\(([\d\.]+)\)|from ([\d\.]+):`)
matches := re.FindStringSubmatch(host)
if len(matches) > 1 {
if matches[1] != "" {
ips = append(ips, matches[1])
} else if len(matches) > 2 && matches[2] != "" {
ips = append(ips, matches[2])
}
}
if len(ips) == 0 {
return nil, fmt.Errorf("failed to extract ip from %s", host)
}
return ips, nil
}
func IsValidIP(ip string) bool {
return net.ParseIP(ip) != nil
}
func ExtractIPAddress(addr string) string {
var ip string
scanner := bufio.NewScanner(strings.NewReader(addr))
for scanner.Scan() {
line := scanner.Text()
if !strings.Contains(line, "inet ") {
continue
}
ip = line
break
}
ip = strings.TrimSpace(ip)
fields := strings.Split(ip, " ")
ips := strings.Split(fields[1], "/")
return ips[0]
}