简体   繁体   English

golang 强制 net/http 客户端使用 IPv4/IPv6

[英]golang force net/http client to use IPv4 / IPv6

I' using go1.11 net/http and want to decect if a domain is ipv6-only.我使用 go1.11 net/http 并且想判断域是否仅支持 ipv6。

What did you do?你做了什么?

I create my own DialContext because want I to detect if a domain is ipv6-only.我创建了自己的 DialContext,因为我想检测域是否仅支持 ipv6。 code below下面的代码

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();
}

I do not now how to close the request even when I can get into network == "ipv4" .即使我可以进入network == "ipv4"我现在也不知道如何关闭请求。

addition添加

Python can solve the question via Force requests to use IPv4 / IPv6 . Python 可以通过Force requests to use IPv4 / IPv6来解决这个问题。 I do not know how to do it in golang.我不知道如何在 golang 中做到这一点。 Could someone one help me?有人可以帮助我吗? Thanks so much!非常感谢!

The network passed to the Control function is either tcp4 for an IPv4 connection or tcp6 for an IPv6 connection, if you are making an outgoing TCP connection.network传递给Control功能或者是tcp4的IPv4连接或tcp6对于IPv6连接,如果你正在传出TCP连接。

From the comments on type Dialer :type Dialer的评论:

 // 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".

(In case of non-TCP connection, other strings are possible.) (在非 TCP 连接的情况下,其他字符串也是可能的。)

Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and "unixpacket".已知网络有“tcp”、“tcp4”(仅 IPv4)、“tcp6”(仅 IPv6)、“udp”、“udp4”(仅 IPv4)、“udp6”(仅 IPv6)、“ip” 、“ip4”(仅 IPv4)、“ip6”(仅 IPv6)、“unix”、“unixgram”和“unixpacket”。

Oh.哦。 I fixed the problem myself.我自己解决了这个问题。

We could not configure force ipv6 connection because it is hard coded我们无法配置强制 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 .) 代码在这里。)

I add a ipv6only flag for transport.go, a getTcpString() and it works.我为 transport.go 添加了一个 ipv6only 标志,一个 getTcpString() 并且它可以工作。

( diff here ) 这里不同

I think even when the server is listening on "tcp6", client running net.Dial using "tcp" should work fine - in dual stack setups.我认为即使服务器正在侦听“tcp6”,使用“tcp”运行 net.Dial 的客户端也应该可以正常工作 - 在双栈设置中。

When I ran into this issue, the culprit was /etc/hosts.当我遇到这个问题时,罪魁祸首是 /etc/hosts。 It had an entry for the host which was mapped to an ipv4 address instead of ipv6 address.它有一个映射到 ipv4 地址而不是 ipv6 地址的主机条目。 This lead to connection refusal.这会导致连接拒绝。

My test example as follows:我的测试示例如下:

Client Code:客户代码:

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")
}

Server Code:服务器代码:

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

This code should work fine with clean /etc/hosts file on dual stack setups.这段代码应该可以在双栈设置中使用干净的 /etc/hosts 文件正常工作。

Thanks.谢谢。

This works for me with go1.17.1:这对 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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM