Select source interface for outgoing HTTP requests - http

I'm trying to make an HTTP request using a specific interface on my machine.
My actual code is:
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
func PanicErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
ief, err := net.InterfaceByName("usb0")
if err != nil {
log.Fatal(err)
}
addrs, err := ief.Addrs()
if err != nil {
log.Fatal(err)
}
tcpAddr := &net.TCPAddr{
IP: addrs[0].(*net.IPNet).IP,
}
dialer := net.Dialer{LocalAddr: tcpAddr}
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
contextTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
conn, err := dialer.DialContext(contextTimeout, network, addr)
return conn, err
}
transport := &http.Transport{DialContext: dialContext}
client := &http.Client{
Transport: transport,
}
// http request
response, err := client.Get("https://ipinfo.io/")
PanicErr(err)
data, err := ioutil.ReadAll(response.Body)
PanicErr(err)
fmt.Println(string(data))
}
But it doesn't work. Once started, as output, I have:
> go run req.go
panic: Get "https://ipinfo.io/": dial tcp 192.168.8.100:0->34.117.59.81:443: i/o timeout
But if I attempt the same request with curl, it works and the IP address is the one the interface have right now:
> curl --interface usb0 https://ipinfo.io/
{
"ip": "31.*.*.*",
...
}
Actual ifconfig output:
usb0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.8.100 netmask 255.255.255.0 broadcast 192.168.8.255
inet6 fe80::21d5:1667:629c:7e91 prefixlen 64 scopeid 0x20<link>
ether 62:ef:7f:**:**:** txqueuelen 1000 (Ethernet)
RX packets 281 bytes 57933 (56.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 73 bytes 8688 (8.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Output of lsusb:
Bus 001 Device 009: ID 12d1:14db Huawei Technologies Co., Ltd. E353/E3131
What's wrong with my code?

Related

Go: Missing port in address

I was trying to make a simple webapp in Go/Golang. My main fuction code is :
func main() {
fileServer := http.FileServer(http.Dir("./static"))
http.Handle("/", fileServer)
http.HandleFunc("/form", formHandler)
http.HandleFunc("/hello", helloHandler)
fmt.Printf("Starting server at port 8080\n")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
When I run the go run main.go I get the following error:
listen tcp: address ;8080: missing port in address
exit status 1
I tried using the lsof -i :8080 command but got no output. Chaging the port to :80 from :8080 had no effect. nc -l :8080 didn't work too.
How do I resolve this issue?
PS: I'm using Fedora OS
Try this code instead:
func main() {
fileServer := http.FileServer(http.Dir("./static"))
httpf := http.NewServeMux()
httpf.Handle("/", fileServer)
httpf.HandleFunc("/form", settings.Grp_Insert)
httpf.HandleFunc("/hello", settings.Grp_Insert)
srv := &http.Server{
ReadTimeout: 3 * time.Second,
WriteTimeout: 6 * time.Second,
Addr: "localhost:8080",
IdleTimeout: 15 * time.Second,
ReadHeaderTimeout: 3 * time.Second,
Handler: httpf,
}
fmt.Printf("Starting server at port 8080\n")
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}

Specify local IP address for HTTP requests (error: mismatched local address type) [duplicate]

I'm getting this error with the following code: dial tcp: mismatched local address type 172.29.4.175
Any idea on how to fix this? Couldn't find anything useful online other than http://oocms.org/question/763660/dial-with-a-specific-address-interface-golang but that didn't work.
The IP 172.29.4.175 is currently the IP of my Macbooks wifi interface.
package main
import (
"fmt"
"net"
"net/http"
)
var url = "https://httpbin.org/get"
func main() {
q := net.ParseIP("172.29.4.175")
addr := &net.IPAddr{q, ""}
var transport = &http.Transport{
DialContext: (&net.Dialer{
LocalAddr: addr,
}).DialContext,
}
var httpclient = &http.Client{
Transport: transport,
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
}
req.Header.Set("User-Agent", "Test-Agent")
resp, err := httpclient.Do(req)
fmt.Println(resp, err)
}
Nearly 100% of the time, an HTTP Dial is going to be connecting via TCP. You're only providing an IP address with the ip network type, yet a TCP address required the tcp network type and a port number.
You can either substitute net.TCPAddr for net.IPAddr, or start with net.ResolveTCPAddr to create the correct type.
addr := &net.TCPAddr{net.IP{172, 29, 4, 175}, 0, ""}
or
addr, _ := net.ResolveTCPAddr("tcp", "172.29.4.175:0")

Cannot use socks5 proxy in golang - read: connection reset by peer

I have a dockerfile which runs tor -
FROM alpine:edge
RUN apk update && apk add tor
EXPOSE 9050
USER tor
CMD ["/usr/bin/tor"]
and ran it using command - docker run --name tor -p 11000:9050 tor
and checked connection using - telnet 127.0.0.1 11000 and it showed connected
Now I want to use tor as proxy while any request from go program. I tried -
package main
import (
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
proxyUrl, err := url.Parse("socks5://127.0.0.1:11000")
if err != nil {
// TODO handle me
panic(err)
}
cl := http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
},
Timeout: 18000 * time.Millisecond,
}
resp, err := cl.Get("http://google.com")
if err != nil {
// TODO handle me
panic(err)
}
// TODO work with the response
fmt.Println(resp)
}
But running this program threw error -
panic: Get http://google.com: socks connect tcp 127.0.0.1:11000->google.com:80: read tcp 127.0.0.1:59630->127.0.0.1:11000: read: connection reset by peer
goroutine 1 [running]: <stacktrace>
exit status 2
I tried other approaches also, notably mentioned here and here but kept getting same error - read: connection reset by peer
Please help which part is incorrect here.
Thanks.
--------------------another approach that I tried ----------------
As mentioned in one of the links, I tried this code also -
const (
PROXY_ADDR = "127.0.0.1:11000"
URL = "http://facebookcorewwwi.onion"
)
func main() {
// create a socks5 dialer
dialer, err := proxy.SOCKS5("tcp", PROXY_ADDR, nil, proxy.Direct)
if err != nil {
fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err)
os.Exit(1)
}
dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
// do anything with ctx
return dialer.Dial(network, address)
}
// setup a http client
httpTransport := &http.Transport{
DialContext: dialContext,
}
httpClient := &http.Client{Transport: httpTransport}
// create a request
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
fmt.Fprintln(os.Stderr, "can't create request:", err)
os.Exit(2)
}
resp, err := httpClient.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, "cannot make get request: ", err)
os.Exit(2)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Fprintln(os.Stderr, "cannot read response body: ", err)
os.Exit(2)
}
fmt.Println("received response -> ", body)
}
but received error -
cannot make get request: Get http://facebookcorewwwi.onion: socks connect tcp 127.0.0.1:11000->facebookcorewwwi.onion:80: read tcp 127.0.0.1:59826->127.0.0.1:11000: read: connection reset by peer
exit status 2
Any help is appreciable.
After making sure tor is working properly on port 9050.
Try the following curl command to ensure tor is working properly.
curl --socks5 localhost:9050 --socks5-hostname localhost:9050 -s https://wtfismyip.com/json
Can you try this
package main
import (
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"golang.org/x/net/proxy"
)
func main() {
proxyUrl := "127.0.0.1:9050"
dialer, err := proxy.SOCKS5("tcp", proxyUrl, nil, proxy.Direct)
dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
return dialer.Dial(network, address)
}
transport := &http.Transport{DialContext: dialContext,
DisableKeepAlives: true}
cl := &http.Client{Transport: transport}
resp, err := cl.Get("https://wtfismyip.com/json")
if err != nil {
// TODO handle me
panic(err)
}
body, err := ioutil.ReadAll(resp.Body)
// TODO work with the response
if err != nil {
fmt.Println("body read failed")
}
fmt.Println(string(body))
}
As suggested in the answer/comment above, main problem was socks5 connection to dockerized tor container.
Found the solution here, i just had to modify dockerfile like -
FROM alpine:edge
RUN apk update && apk add tor
RUN cp /etc/tor/torrc.sample /etc/tor/torrc && echo "SocksPort 0.0.0.0:9050" > /etc/tor/torrc
EXPOSE 9050
CMD ["/usr/bin/tor"]
Other code pieces are working fine.

How do I find the remote IP address for a Go http.Response?

The http.Request struct includes the remote IP and port of the request's sender:
// RemoteAddr allows HTTP servers and other software to record
// the network address that sent the request, usually for
// logging. This field is not filled in by ReadRequest and
// has no defined format. The HTTP server in this package
// sets RemoteAddr to an "IP:port" address before invoking a
// handler.
// This field is ignored by the HTTP client.
**RemoteAddr string**
The http.Response object has no such field.
I would like to know the IP address that responded to the request I sent, even when I sent it to a DNS address.
I thought that net.LookupHost() might be helpful, but 1) it can return multiple IPs for a single host name, and 2) it ignores the hosts file unless cgo is available, which it is not in my case.
Is it possible to retrieve the remote IP address for an http.Response?
Use the net/http/httptrace package and use the GotConnInfo hook to capture the net.Conn and its corresponding Conn.RemoteAddr().
This will give you the address the Transport actually dialled, as opposed to what was resolved in DNSDoneInfo:
package main
import (
"log"
"net/http"
"net/http/httptrace"
)
func main() {
req, err := http.NewRequest("GET", "https://example.com/", nil)
if err != nil {
log.Fatal(err)
}
trace := &httptrace.ClientTrace{
GotConn: func(connInfo httptrace.GotConnInfo) {
log.Printf("resolved to: %s", connInfo.Conn.RemoteAddr())
},
}
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
client := &http.Client{}
_, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
}
Outputs:
~ go run ip.go
2017/02/18 19:38:11 resolved to: 104.16.xx.xxx:443
Another solution I came up with was the hook the DialContext function in the http client transport. This is a specific solution that lets you modify the http.Client instead of the request which may be useful.
We first create a function that returns a hooked dial context
func remoteAddressDialHook(remoteAddressPtr *net.Addr) func(ctx context.Context, network string, address string) (net.Conn, error) {
hookedDialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
originalDialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
conn, err := originalDialer.DialContext(ctx, network, address)
if err != nil {
return nil, err
}
// conn was successfully created
*remoteAddressPtr = conn.RemoteAddr()
return conn, err
}
return hookedDialContext
}
We can then use this function to create a DialContext that writes to an outparameter
var remoteAddr net.Addr
customTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: remoteAddressDialHook(&remoteAddr),
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
customHttpClient := http.Client{
Transport: customTransport,
}
// do what you normally would with a http client, it will then set the remoteAddr to be the remote address
fmt.Println(remoteAddr.String())

Golang TCP connection close doesn't give an EOF

This is actually a follow up on this question: How to know TCP connection is closed in Golang net package? which doesn't work in my implementation, and I'm trying to figure out why.
The idea is, I have a server that accepts TCP connections and waits for the client to close them. It waits for 5 seconds, and every second it tries to read from the TCP connection, if this gives an EOF error, that would indicate that the client has closed the connection.
This is the servers code:
func server(done chan bool) {
l, _ := net.Listen("tcp", serverAddress)
// Listen for incomming TCP connections
conn, err := l.Accept()
if err != nil {
log.Fatalf("l.Accept(): got error on accept: %v", err)
}
defer conn.Close()
// Read periodically from the TCP connection to see if we can find
// out when the client has closed his end.
go func() {
for {
conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
b, err := conn.Read([]byte{})
fmt.Printf("Read %v bytes with err %v \n", b, err)
if err == io.EOF {
conn.Close()
conn = nil
break
} else {
var zero time.Time
conn.SetReadDeadline(zero)
}
time.Sleep(1 * time.Second)
}
done <- true
}()
// Do this for 5 seconds, than quit
time.Sleep(5 * time.Second)
done <- true
}
So the client sets up a connection, waits for two seconds and then closes it. The clients code is as follows:
func main() {
done := make(chan bool, 1)
go server(done)
// Give the listener some time to set up
time.Sleep(200 * time.Millisecond)
// Dial the server
conn, err := net.Dial("tcp", serverAddress)
if err != nil {
log.Fatalf("net.Dial(): got %v, want no error", err)
}
defer conn.Close()
go func() {
// After two seconds, close the TCP connection to the server
time.Sleep(2 * time.Second)
fmt.Printf("Client closes conn now with err %v\n", conn.Close())
}()
// Wait untill server is done
<-done
}
I would expect that the server would get an EOF error when reading, after the client has closed the connection, however the output I get is:
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
Client closes conn now with err <nil>
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
I have the wireshark output here. It actually shows the FIN was send by the client (package 11), but only 3 seconds later the server sends it's own FIN:
Wireshark output
So basically I can't figure out why I'm not getting any error on the read on the servers side.

Resources