golang force net/http client to use IPv4 / IPv6

五迷三道 提交于 2020-06-12 07:41:10

问题


I' using go1.11 net/http and want to decect if a domain is ipv6-only.

What did you do?

I create my own DialContext because want I to detect if a domain is ipv6-only. 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".

addition

Python can solve the question via Force requests to use IPv4 / IPv6. I do not know how to do it in golang. Could someone one help me? Thanks so much!


回答1:


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.

From the comments on 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.)

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




回答2:


Oh. I fixed the problem myself.

We could not configure force ipv6 connection because it is hard coded

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

(diff here)




回答3:


I think even when the server is listening on "tcp6", client running net.Dial using "tcp" should work fine - in dual stack setups.

When I ran into this issue, the culprit was /etc/hosts. It had an entry for the host which was mapped to an ipv4 address instead of ipv6 address. 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.

Thanks.



来源:https://stackoverflow.com/questions/53055808/golang-force-net-http-client-to-use-ipv4-ipv6

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!