185 lines
3.8 KiB
Go
Executable File
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]
|
|
}
|