github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/netx/local.go (about)

     1  package netx
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/bingoohuang/gg/pkg/goip"
     9  	"github.com/bingoohuang/gg/pkg/netx/freeport"
    10  	"github.com/bingoohuang/gg/pkg/ss"
    11  	"github.com/bingoohuang/golog/pkg/randx"
    12  	"github.com/go-resty/resty/v2"
    13  )
    14  
    15  // IsLocalAddr 判断addr(ip,域名等)是否指向本机
    16  // 由于IP可能经由 iptable 指向,或者可能是域名,或者其它,不能直接与本机IP做对比
    17  // 本方法构建一个临时的HTTP服务,然后使用指定的addr去连接改HTTP服务,如果能连接上,说明addr是指向本机的地址
    18  func IsLocalAddr(addr string) (bool, error) {
    19  	if addr == "localhost" || addr == "127.0.0.1" || addr == "::1" {
    20  		return true, nil
    21  	}
    22  
    23  	if ss.AnyOf(addr, goip.ListIfaceNames()...) {
    24  		return true, nil
    25  	}
    26  
    27  	port, err := freeport.PortE()
    28  	if err != nil {
    29  		return false, err
    30  	}
    31  
    32  	radStr := randx.String(512)
    33  	mux := http.NewServeMux()
    34  	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
    35  		_, _ = fmt.Fprint(w, radStr)
    36  	})
    37  
    38  	server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: mux}
    39  
    40  	go func() { _ = server.ListenAndServe() }()
    41  
    42  	time.Sleep(100 * time.Millisecond) // nolint gomnd
    43  
    44  	addr = `http://` + JoinHostPort(addr, port)
    45  	resp, err := resty.New().SetTimeout(3 * time.Second).R().Get(addr)
    46  	_ = server.Close()
    47  
    48  	if err != nil {
    49  		return false, err
    50  	}
    51  
    52  	return resp.String() == radStr, nil
    53  }
    54  
    55  // JoinHostPort make IP:Port for ipv4/domain or [IPv6]:Port for ipv6.
    56  func JoinHostPort(host string, port int) string {
    57  	if goip.IsIPv6(host) {
    58  		return fmt.Sprintf("[%s]:%d", host, port)
    59  	}
    60  
    61  	return fmt.Sprintf("%s:%d", host, port)
    62  }