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] }