1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package net
6
7import (
8	"fmt"
9	"internal/testenv"
10	"io"
11	"strings"
12	"testing"
13)
14
15func TestResolveGoogle(t *testing.T) {
16	testenv.MustHaveExternalNetwork(t)
17
18	if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
19		t.Skip("both IPv4 and IPv6 are required")
20	}
21
22	for _, network := range []string{"tcp", "tcp4", "tcp6"} {
23		addr, err := ResolveTCPAddr(network, "www.google.com:http")
24		if err != nil {
25			t.Error(err)
26			continue
27		}
28		switch {
29		case network == "tcp" && addr.IP.To4() == nil:
30			fallthrough
31		case network == "tcp4" && addr.IP.To4() == nil:
32			t.Errorf("got %v; want an IPv4 address on %s", addr, network)
33		case network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil):
34			t.Errorf("got %v; want an IPv6 address on %s", addr, network)
35		}
36	}
37}
38
39var dialGoogleTests = []struct {
40	dial               func(string, string) (Conn, error)
41	unreachableNetwork string
42	networks           []string
43	addrs              []string
44}{
45	{
46		dial:     (&Dialer{DualStack: true}).Dial,
47		networks: []string{"tcp", "tcp4", "tcp6"},
48		addrs:    []string{"www.google.com:http"},
49	},
50	{
51		dial:               Dial,
52		unreachableNetwork: "tcp6",
53		networks:           []string{"tcp", "tcp4"},
54	},
55	{
56		dial:               Dial,
57		unreachableNetwork: "tcp4",
58		networks:           []string{"tcp", "tcp6"},
59	},
60}
61
62func TestDialGoogle(t *testing.T) {
63	testenv.MustHaveExternalNetwork(t)
64
65	if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
66		t.Skip("both IPv4 and IPv6 are required")
67	}
68
69	var err error
70	dialGoogleTests[1].addrs, dialGoogleTests[2].addrs, err = googleLiteralAddrs()
71	if err != nil {
72		t.Error(err)
73	}
74	for _, tt := range dialGoogleTests {
75		for _, network := range tt.networks {
76			disableSocketConnect(tt.unreachableNetwork)
77			for _, addr := range tt.addrs {
78				if err := fetchGoogle(tt.dial, network, addr); err != nil {
79					t.Error(err)
80				}
81			}
82			enableSocketConnect()
83		}
84	}
85}
86
87var (
88	literalAddrs4 = [...]string{
89		"%d.%d.%d.%d:80",
90		"www.google.com:80",
91		"%d.%d.%d.%d:http",
92		"www.google.com:http",
93		"%03d.%03d.%03d.%03d:0080",
94		"[::ffff:%d.%d.%d.%d]:80",
95		"[::ffff:%02x%02x:%02x%02x]:80",
96		"[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80",
97		"[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80",
98		"[0:0:0:0::ffff:%d.%d.%d.%d]:80",
99	}
100	literalAddrs6 = [...]string{
101		"[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80",
102		"ipv6.google.com:80",
103		"[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http",
104		"ipv6.google.com:http",
105	}
106)
107
108func googleLiteralAddrs() (lits4, lits6 []string, err error) {
109	ips, err := LookupIP("www.google.com")
110	if err != nil {
111		return nil, nil, err
112	}
113	if len(ips) == 0 {
114		return nil, nil, nil
115	}
116	var ip4, ip6 IP
117	for _, ip := range ips {
118		if ip4 == nil && ip.To4() != nil {
119			ip4 = ip.To4()
120		}
121		if ip6 == nil && ip.To16() != nil && ip.To4() == nil {
122			ip6 = ip.To16()
123		}
124		if ip4 != nil && ip6 != nil {
125			break
126		}
127	}
128	if ip4 != nil {
129		for i, lit4 := range literalAddrs4 {
130			if strings.Contains(lit4, "%") {
131				literalAddrs4[i] = fmt.Sprintf(lit4, ip4[0], ip4[1], ip4[2], ip4[3])
132			}
133		}
134		lits4 = literalAddrs4[:]
135	}
136	if ip6 != nil {
137		for i, lit6 := range literalAddrs6 {
138			if strings.Contains(lit6, "%") {
139				literalAddrs6[i] = fmt.Sprintf(lit6, ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15])
140			}
141		}
142		lits6 = literalAddrs6[:]
143	}
144	return
145}
146
147func fetchGoogle(dial func(string, string) (Conn, error), network, address string) error {
148	c, err := dial(network, address)
149	if err != nil {
150		return err
151	}
152	defer c.Close()
153	req := []byte("GET /robots.txt HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
154	if _, err := c.Write(req); err != nil {
155		return err
156	}
157	b := make([]byte, 1000)
158	n, err := io.ReadFull(c, b)
159	if err != nil {
160		return err
161	}
162	if n < 1000 {
163		return fmt.Errorf("short read from %s:%s->%s", network, c.RemoteAddr(), c.LocalAddr())
164	}
165	return nil
166}
167