【问题标题】:golang force net/http client to use IPv4 / IPv6golang 强制 net/http 客户端使用 IPv4 / IPv6
【发布时间】:2018-10-30 00:39:15
【问题描述】:

我使用 go1.11 net/http 并想检测一个域是否仅限 ipv6。

你做了什么?

我创建自己的 DialContext 是因为我想检测一个域是否是 ipv6-only。代码如下

package main
import (
    "errors"
    "fmt"
    "net"
    "net/http"
    "syscall"
    "time"
)
func ModifiedTransport() {
    var MyTransport = &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
            DualStack: false,
            Control: func(network, address string, c syscall.RawConn) error {
                if network == "ipv4" {
                    // I want to  cancel connection here client.Get("http://myexample.com") return a non-nil err.
                    return errors.New("you should not use ipv4")
                }
                return nil
            },
        }).DialContext,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
    var myClient = http.Client{Transport: MyTransport}
    resp, myerr := myClient.Get("http://www.github.com")
    if myerr != nil {
        fmt.Println("request error")
        return 
    }
    var buffer = make([]byte, 1000)
    resp.Body.Read(buffer)
    fmt.Println(string(buffer))
}
func main(){
    ModifiedTransport();
}

即使我可以进入network == "ipv4",我现在也不知道如何关闭请求。

加法

Python 可以通过Force requests to use IPv4 / IPv6 解决这个问题。我不知道如何在golang中做到这一点。有人可以帮助我吗? 非常感谢!

【问题讨论】:

    标签: go web-crawler


    【解决方案1】:

    如果您要进行传出 TCP 连接,传递给 Control 函数的 network 要么是用于 IPv4 连接的 tcp4,要么是用于 IPv6 连接的 tcp6

    来自type Dialer的cmets:

            // Network and address parameters passed to Control method are not
            // necessarily the ones passed to Dial. For example, passing "tcp" to Dial
            // will cause the Control function to be called with "tcp4" or "tcp6".
    

    (在非TCP连接的情况下,other strings是可能的。)

    已知网络有“tcp”、“tcp4”(仅限 IPv4)、“tcp6”(仅限 IPv6)、“udp”、“udp4”(仅限 IPv4)、“udp6”(仅限 IPv6)、 “ip”、“ip4”(仅限 IPv4)、“ip6”(仅限 IPv6)、“unix”、“unixgram”和“unixpacket”。

    【讨论】:

    • 感谢您的回复。但是你知道如何配置http Transport/DialContext 让Control 只接收ipv6 连接吗?
    【解决方案2】:

    哦。我自己解决了这个问题。

    我们无法配置强制 ipv6 连接,因为它是硬编码的

    ...
    if cm.scheme() == "https" && t.DialTLS != nil {
            var err error
            pconn.conn, err = t.DialTLS("tcp", cm.addr())
            if err != nil {
                return nil, wrapErr(err)
            }
    ...
    

    (code here.)

    我为 transport.go 添加了一个 ipv6only 标志,一个 getTcpString() 并且它可以工作。

    (diff here)

    【讨论】:

      【解决方案3】:

      我认为即使服务器正在侦听“tcp6”,运行 net.Dial 使用“tcp”的客户端也应该可以正常工作 - 在双堆栈设置中。

      当我遇到这个问题时,罪魁祸首是 /etc/hosts。它有一个映射到 ipv4 地址而不是 ipv6 地址的主机条目。这会导致连接被拒绝。

      我的测试示例如下:

      客户代码:

      package main
      
      import "net"
      import "fmt"
      
      func main() {
          conn, err := net.Dial("tcp", "hostname:9876")
          if err != nil {
              fmt.Println("Error in net.Dial", err)
              return
          }
      
          conn.Close()
          fmt.Println("Successful")
      }
      

      服务器代码:

      package main
      
      import "net"
      import "fmt"
      
      func main() {
          lis, err := net.Listen("tcp6", ":9876")
          if err != nil {
              fmt.Println("Error in listen", err)
              return
          }
      
          for {
              _, err := lis.Accept()
              if err != nil {
                  fmt.Println("Error in Accept", err)
                  return
              }
          }
      }
      

      此代码应该可以在双堆栈设置上与干净的 /etc/hosts 文件一起正常工作。

      谢谢。

      【讨论】:

        【解决方案4】:

        这适用于 go1.17.1:

        var (
            zeroDialer net.Dialer
            httpClient = &http.Client{
                Timeout: 10 * time.Second,
            }
        )
        
        func init() {
            transport := http.DefaultTransport.(*http.Transport).Clone()
            transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
                return zeroDialer.DialContext(ctx, "tcp4", addr)
            }
            httpClient.Transport = transport
        }
        

        【讨论】:

          猜你喜欢
          • 2018-08-13
          • 2016-01-07
          • 2016-06-10
          • 1970-01-01
          • 2020-08-27
          • 1970-01-01
          • 1970-01-01
          • 2016-03-28
          • 1970-01-01
          相关资源
          最近更新 更多