mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
host and port name lookup
R=r,presotto DELTA=1239 (935 added, 281 deleted, 23 changed) OCL=21041 CL=21539
This commit is contained in:
parent
e29ce175ed
commit
83348f956e
15 changed files with 886 additions and 244 deletions
|
|
@ -3,7 +3,8 @@
|
|||
# license that can be found in the LICENSE file.
|
||||
|
||||
# DO NOT EDIT. Automatically generated by gobuild.
|
||||
# gobuild -m fd_darwin.go fd.go net.go net_darwin.go ip.go dnsmsg.go >Makefile
|
||||
# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_darwin.go\
|
||||
# ip.go net.go net_darwin.go parse.go port.go >Makefile
|
||||
O=6
|
||||
GC=$(O)g
|
||||
CC=$(O)c -w
|
||||
|
|
@ -32,37 +33,55 @@ coverage: packages
|
|||
$(AS) $*.s
|
||||
|
||||
O1=\
|
||||
fd_$(GOOS).$O\
|
||||
ip.$O\
|
||||
dnsmsg.$O\
|
||||
fd_$(GOOS).$O\
|
||||
parse.$O\
|
||||
|
||||
O2=\
|
||||
fd.$O\
|
||||
net_$(GOOS).$O\
|
||||
ip.$O\
|
||||
port.$O\
|
||||
|
||||
O3=\
|
||||
dnsconfig.$O\
|
||||
net_$(GOOS).$O\
|
||||
|
||||
O4=\
|
||||
net.$O\
|
||||
|
||||
net.a: a1 a2 a3
|
||||
O5=\
|
||||
dnsclient.$O\
|
||||
|
||||
net.a: a1 a2 a3 a4 a5
|
||||
|
||||
a1: $(O1)
|
||||
$(AR) grc net.a fd_$(GOOS).$O ip.$O dnsmsg.$O
|
||||
$(AR) grc net.a dnsmsg.$O fd_$(GOOS).$O parse.$O
|
||||
rm -f $(O1)
|
||||
|
||||
a2: $(O2)
|
||||
$(AR) grc net.a fd.$O net_$(GOOS).$O
|
||||
$(AR) grc net.a fd.$O ip.$O port.$O
|
||||
rm -f $(O2)
|
||||
|
||||
a3: $(O3)
|
||||
$(AR) grc net.a net.$O
|
||||
$(AR) grc net.a dnsconfig.$O net_$(GOOS).$O
|
||||
rm -f $(O3)
|
||||
|
||||
a4: $(O4)
|
||||
$(AR) grc net.a net.$O
|
||||
rm -f $(O4)
|
||||
|
||||
a5: $(O5)
|
||||
$(AR) grc net.a dnsclient.$O
|
||||
rm -f $(O5)
|
||||
|
||||
newpkg: clean
|
||||
$(AR) grc net.a
|
||||
|
||||
$(O1): newpkg
|
||||
$(O2): a1
|
||||
$(O3): a2
|
||||
$(O4): a3
|
||||
$(O5): a4
|
||||
|
||||
nuke: clean
|
||||
rm -f $(GOROOT)/pkg/net.a
|
||||
|
|
|
|||
89
src/lib/net/dialgoogle_test.go
Normal file
89
src/lib/net/dialgoogle_test.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net";
|
||||
"flag";
|
||||
"io";
|
||||
"os";
|
||||
"testing";
|
||||
)
|
||||
|
||||
// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
|
||||
var ipv6 = false
|
||||
var ipv6_flag = flag.Bool("ipv6", false, &ipv6, "assume ipv6 tunnel is present")
|
||||
|
||||
// fd is already connected to www.google.com port 80.
|
||||
// Run an HTTP request to fetch the main page.
|
||||
func FetchGoogle(t *testing.T, fd net.Conn, network, addr string) {
|
||||
req := io.StringBytes("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
|
||||
n, errno := fd.Write(req);
|
||||
|
||||
buf := new([1000]byte);
|
||||
n, errno = io.Readn(fd, buf);
|
||||
|
||||
if n < 1000 {
|
||||
t.Errorf("FetchGoogle: short HTTP read from %s %s", network, addr);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func DoDial(t *testing.T, network, addr string) {
|
||||
fd, err := net.Dial(network, "", addr);
|
||||
if err != nil {
|
||||
t.Errorf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err);
|
||||
return
|
||||
}
|
||||
FetchGoogle(t, fd, network, addr);
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
func DoDialTCP(t *testing.T, network, addr string) {
|
||||
fd, err := net.DialTCP(network, "", addr);
|
||||
if err != nil {
|
||||
t.Errorf("net.DialTCP(%q, %q, %q) = _, %v", network, "", addr, err);
|
||||
} else {
|
||||
FetchGoogle(t, fd, network, addr);
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
var googleaddrs = []string {
|
||||
"74.125.19.99:80",
|
||||
"www.google.com:80",
|
||||
"74.125.19.99:http",
|
||||
"www.google.com:http",
|
||||
"074.125.019.099:0080",
|
||||
"[::ffff:74.125.19.99]:80",
|
||||
"[::ffff:4a7d:1363]:80",
|
||||
"[0:0:0:0:0000:ffff:74.125.19.99]:80",
|
||||
"[0:0:0:0:000000:ffff:74.125.19.99]:80",
|
||||
"[0:0:0:0:0:ffff::74.125.19.99]:80",
|
||||
"[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set
|
||||
}
|
||||
|
||||
export func TestDialGoogle(t *testing.T) {
|
||||
// If no ipv6 tunnel, don't try the last address.
|
||||
if !ipv6 {
|
||||
googleaddrs[len(googleaddrs)-1] = ""
|
||||
}
|
||||
|
||||
for i := 0; i < len(googleaddrs); i++ {
|
||||
addr := googleaddrs[i];
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
t.Logf("-- %s --", addr);
|
||||
DoDial(t, "tcp", addr);
|
||||
DoDialTCP(t, "tcp", addr);
|
||||
if addr[0] != '[' {
|
||||
DoDial(t, "tcp4", addr);
|
||||
DoDialTCP(t, "tcp4", addr)
|
||||
}
|
||||
DoDial(t, "tcp6", addr);
|
||||
DoDialTCP(t, "tcp6", addr)
|
||||
}
|
||||
}
|
||||
215
src/lib/net/dnsclient.go
Normal file
215
src/lib/net/dnsclient.go
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// DNS client.
|
||||
// Has to be linked into package net for Dial.
|
||||
|
||||
// TODO(rsc):
|
||||
// Check periodically whether /etc/resolv.conf has changed.
|
||||
// Could potentially handle many outstanding lookups faster.
|
||||
// Could have a small cache.
|
||||
// Random UDP source port (net.Dial should do that for us).
|
||||
// Random request IDs.
|
||||
// More substantial error reporting.
|
||||
// Remove use of fmt?
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"fmt";
|
||||
"io";
|
||||
"net";
|
||||
"once";
|
||||
"os";
|
||||
"strings";
|
||||
)
|
||||
|
||||
export var (
|
||||
DNS_InternalError = os.NewError("internal dns error");
|
||||
DNS_MissingConfig = os.NewError("no dns configuration");
|
||||
DNS_NoAnswer = os.NewError("dns got no answer");
|
||||
DNS_BadRequest = os.NewError("malformed dns request");
|
||||
DNS_BadReply = os.NewError("malformed dns reply");
|
||||
DNS_ServerFailure = os.NewError("dns server failure");
|
||||
DNS_NoServers = os.NewError("no dns servers");
|
||||
DNS_NameTooLong = os.NewError("dns name too long");
|
||||
DNS_RedirectLoop = os.NewError("dns redirect loop");
|
||||
DNS_NameNotFound = os.NewError("dns name not found");
|
||||
);
|
||||
|
||||
// Send a request on the connection and hope for a reply.
|
||||
// Up to cfg.attempts attempts.
|
||||
func Exchange(cfg *DNS_Config, c Conn, name string) (m *DNS_Msg, err *os.Error) {
|
||||
if len(name) >= 256 {
|
||||
return nil, DNS_NameTooLong
|
||||
}
|
||||
out := new(DNS_Msg);
|
||||
out.id = 0x1234;
|
||||
out.question = &[]DNS_Question{
|
||||
DNS_Question{ name, DNS_TypeA, DNS_ClassINET }
|
||||
};
|
||||
out.recursion_desired = true;
|
||||
msg, ok := out.Pack();
|
||||
if !ok {
|
||||
return nil, DNS_InternalError
|
||||
}
|
||||
|
||||
for attempt := 0; attempt < cfg.attempts; attempt++ {
|
||||
n, err := c.Write(msg);
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(rsc): set up timeout or call ReadTimeout.
|
||||
// right now net does not support that.
|
||||
|
||||
buf := new([]byte, 2000); // More than enough.
|
||||
n, err = c.Read(buf);
|
||||
if err != nil {
|
||||
// TODO(rsc): only continue if timed out
|
||||
continue
|
||||
}
|
||||
buf = buf[0:n];
|
||||
in := new(DNS_Msg);
|
||||
if !in.Unpack(buf) || in.id != out.id {
|
||||
continue
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
return nil, DNS_NoAnswer
|
||||
}
|
||||
|
||||
// Find answer for name in dns message.
|
||||
// On return, if err == nil, addrs != nil.
|
||||
// TODO(rsc): Maybe return *[]*[]byte (==*[]IPAddr) instead?
|
||||
func Answer(name string, dns *DNS_Msg) (addrs *[]string, err *os.Error) {
|
||||
addrs = new([]string, len(dns.answer))[0:0];
|
||||
|
||||
if dns.rcode == DNS_RcodeNameError && dns.authoritative {
|
||||
return nil, DNS_NameNotFound // authoritative "no such host"
|
||||
}
|
||||
if dns.rcode != DNS_RcodeSuccess {
|
||||
// None of the error codes make sense
|
||||
// for the query we sent. If we didn't get
|
||||
// a name error and we didn't get success,
|
||||
// the server is behaving incorrectly.
|
||||
return nil, DNS_ServerFailure
|
||||
}
|
||||
|
||||
// Look for the name.
|
||||
// Presotto says it's okay to assume that servers listed in
|
||||
// /etc/resolv.conf are recursive resolvers.
|
||||
// We asked for recursion, so it should have included
|
||||
// all the answers we need in this one packet.
|
||||
Cname:
|
||||
for cnameloop := 0; cnameloop < 10; cnameloop++ {
|
||||
addrs = addrs[0:0];
|
||||
for i := 0; i < len(dns.answer); i++ {
|
||||
rr := dns.answer[i];
|
||||
h := rr.Header();
|
||||
if h.class == DNS_ClassINET && h.name == name {
|
||||
switch h.rrtype {
|
||||
case DNS_TypeA:
|
||||
n := len(addrs);
|
||||
a := rr.(*DNS_RR_A).a;
|
||||
addrs = addrs[0:n+1];
|
||||
addrs[n] = fmt.sprintf("%d.%d.%d.%d", (a>>24), (a>>16)&0xFF, (a>>8)&0xFF, a&0xFF);
|
||||
case DNS_TypeCNAME:
|
||||
// redirect to cname
|
||||
name = rr.(*DNS_RR_CNAME).cname;
|
||||
continue Cname
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, DNS_NameNotFound
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// Too many redirects
|
||||
return nil, DNS_RedirectLoop
|
||||
}
|
||||
|
||||
// Do a lookup for a single name, which must be rooted
|
||||
// (otherwise Answer will not find the answers).
|
||||
func TryOneName(cfg *DNS_Config, name string) (addrs *[]string, err *os.Error) {
|
||||
err = DNS_NoServers;
|
||||
for i := 0; i < len(cfg.servers); i++ {
|
||||
// Calling Dial here is scary -- we have to be sure
|
||||
// not to dial a name that will require a DNS lookup,
|
||||
// or Dial will call back here to translate it.
|
||||
// The DNS config parser has already checked that
|
||||
// all the cfg.servers[i] are IP addresses, which
|
||||
// Dial will use without a DNS lookup.
|
||||
c, cerr := Dial("udp", "", cfg.servers[i] + ":53");
|
||||
if cerr != nil {
|
||||
err = cerr;
|
||||
continue;
|
||||
}
|
||||
msg, merr := Exchange(cfg, c, name);
|
||||
c.Close();
|
||||
if merr != nil {
|
||||
err = merr;
|
||||
continue;
|
||||
}
|
||||
addrs, aerr := Answer(name, msg);
|
||||
if aerr != nil && aerr != DNS_NameNotFound {
|
||||
err = aerr;
|
||||
continue;
|
||||
}
|
||||
return addrs, aerr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var cfg *DNS_Config
|
||||
|
||||
func LoadConfig() {
|
||||
cfg = DNS_ReadConfig();
|
||||
}
|
||||
|
||||
export func LookupHost(name string) (name1 string, addrs *[]string, err *os.Error) {
|
||||
// TODO(rsc): Pick out obvious non-DNS names to avoid
|
||||
// sending stupid requests to the server?
|
||||
|
||||
once.Do(&LoadConfig);
|
||||
if cfg == nil {
|
||||
err = DNS_MissingConfig;
|
||||
return;
|
||||
}
|
||||
|
||||
// If name is rooted (trailing dot) or has enough dots,
|
||||
// try it by itself first.
|
||||
rooted := len(name) > 0 && name[len(name)-1] == '.';
|
||||
if rooted || strings.count(name, ".") >= cfg.ndots {
|
||||
rname := name;
|
||||
if !rooted {
|
||||
rname += ".";
|
||||
}
|
||||
// Can try as ordinary name.
|
||||
addrs, aerr := TryOneName(cfg, rname);
|
||||
if aerr == nil {
|
||||
return rname, addrs, nil;
|
||||
}
|
||||
err = aerr;
|
||||
}
|
||||
if rooted {
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, try suffixes.
|
||||
for i := 0; i < len(cfg.search); i++ {
|
||||
newname := name+"."+cfg.search[i];
|
||||
if newname[len(newname)-1] != '.' {
|
||||
newname += "."
|
||||
}
|
||||
addrs, aerr := TryOneName(cfg, newname);
|
||||
if aerr == nil {
|
||||
return newname, addrs, nil;
|
||||
}
|
||||
err = aerr;
|
||||
}
|
||||
return
|
||||
}
|
||||
109
src/lib/net/dnsconfig.go
Normal file
109
src/lib/net/dnsconfig.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Read system DNS config from /etc/resolv.conf
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"io";
|
||||
"net";
|
||||
"os";
|
||||
"strconv";
|
||||
)
|
||||
|
||||
export type DNS_Config struct {
|
||||
servers *[]string; // servers to use
|
||||
search *[]string; // suffixes to append to local name
|
||||
ndots int; // number of dots in name to trigger absolute lookup
|
||||
timeout int; // seconds before giving up on packet
|
||||
attempts int; // lost packets before giving up on server
|
||||
rotate bool; // round robin among servers
|
||||
}
|
||||
|
||||
// See resolv.conf(5) on a Linux machine.
|
||||
// TODO(rsc): Supposed to call uname() and chop the beginning
|
||||
// of the host name to get the default search domain.
|
||||
// We assume it's in resolv.conf anyway.
|
||||
export func DNS_ReadConfig() *DNS_Config {
|
||||
file := Open("/etc/resolv.conf");
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
conf := new(DNS_Config);
|
||||
conf.servers = new([]string, 3)[0:0]; // small, but the standard limit
|
||||
conf.search = new([]string, 0);
|
||||
conf.ndots = 1;
|
||||
conf.timeout = 1;
|
||||
conf.attempts = 1;
|
||||
conf.rotate = false;
|
||||
var err *os.Error;
|
||||
for line, ok := file.ReadLine(); ok; line, ok = file.ReadLine() {
|
||||
f := GetFields(line);
|
||||
if len(f) < 1 {
|
||||
continue;
|
||||
}
|
||||
switch f[0] {
|
||||
case "nameserver": // add one name server
|
||||
a := conf.servers;
|
||||
n := len(a);
|
||||
if len(f) > 1 && n < cap(a) {
|
||||
// One more check: make sure server name is
|
||||
// just an IP address. Otherwise we need DNS
|
||||
// to look it up.
|
||||
name := f[1];
|
||||
if ParseIP(name) != nil {
|
||||
a = a[0:n+1];
|
||||
a[n] = name;
|
||||
conf.servers = a;
|
||||
}
|
||||
}
|
||||
|
||||
case "domain": // set search path to just this domain
|
||||
if len(f) > 1 {
|
||||
conf.search = new([]string, 1);
|
||||
conf.search[0] = f[1];
|
||||
} else {
|
||||
conf.search = new([]string, 0)
|
||||
}
|
||||
|
||||
case "search": // set search path to given servers
|
||||
conf.search = new([]string, len(f) - 1);
|
||||
for i := 0; i < len(conf.search); i++ {
|
||||
conf.search[i] = f[i+1];
|
||||
}
|
||||
|
||||
case "options": // magic options
|
||||
for i := 1; i < len(f); i++ {
|
||||
s := f[i];
|
||||
switch {
|
||||
case len(s) >= 6 && s[0:6] == "ndots:":
|
||||
n, i, ok := Dtoi(s, 6);
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
conf.ndots = n;
|
||||
case len(s) >= 8 && s[0:8] == "timeout:":
|
||||
n, i, ok := Dtoi(s, 8);
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
conf.timeout = n;
|
||||
case len(s) >= 8 && s[0:9] == "attempts:":
|
||||
n, i, ok := Dtoi(s, 9);
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
conf.attempts = n;
|
||||
case s == "rotate":
|
||||
conf.rotate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file.Close();
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +63,14 @@ export const (
|
|||
DNS_ClassCHAOS = 3;
|
||||
DNS_ClassHESIOD = 4;
|
||||
DNS_ClassANY = 255;
|
||||
|
||||
// DNS_Msg.rcode
|
||||
DNS_RcodeSuccess = 0;
|
||||
DNS_RcodeFormatError = 1;
|
||||
DNS_RcodeServerFailure = 2;
|
||||
DNS_RcodeNameError = 3;
|
||||
DNS_RcodeNotImplemented = 4;
|
||||
DNS_RcodeRefused = 5;
|
||||
)
|
||||
|
||||
// The wire format for the DNS packet header.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@
|
|||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
export const (
|
||||
IPv4len = 4;
|
||||
IPv6len = 16
|
||||
|
|
@ -240,58 +244,6 @@ export func MaskToString(mask *[]byte) string {
|
|||
return IPToString(mask)
|
||||
}
|
||||
|
||||
// Parsing.
|
||||
|
||||
// Bigger than we need, not too big to worry about overflow
|
||||
const Big = 0xFFFFFF
|
||||
|
||||
// Decimal to integer starting at &s[i].
|
||||
// Returns number, new offset, success.
|
||||
func dtoi(s string, i int) (n int, i1 int, ok bool) {
|
||||
if len(s) <= i || s[i] < '0' || s[i] > '9' {
|
||||
return 0, i, false
|
||||
}
|
||||
n = 0;
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
n = n*10 + int(s[i] - '0');
|
||||
if n >= Big {
|
||||
return 0, i, false
|
||||
}
|
||||
}
|
||||
return n, i, true
|
||||
}
|
||||
|
||||
// Is b a hex digit?
|
||||
func ishex(b byte) bool {
|
||||
return '0' <= b && b <= '9'
|
||||
|| 'a' <= b && b <= 'f'
|
||||
|| 'A' <= b && b <= 'F'
|
||||
}
|
||||
|
||||
// Hexadecimal to integer starting at &s[i].
|
||||
// Returns number, new offset, success.
|
||||
func xtoi(s string, i int) (n int, i1 int, ok bool) {
|
||||
if len(s) <= i || !ishex(s[i]) {
|
||||
return 0, i, false
|
||||
}
|
||||
|
||||
n = 0;
|
||||
for ; i < len(s) && ishex(s[i]); i++ {
|
||||
n *= 16;
|
||||
if '0' <= s[i] && s[i] <= '9' {
|
||||
n += int(s[i] - '0')
|
||||
} else if 'a' <= s[i] && s[i] <= 'f' {
|
||||
n += int(s[i] - 'a') + 10
|
||||
} else {
|
||||
n += int(s[i] -'A') + 10
|
||||
}
|
||||
if n >= Big {
|
||||
return 0, i, false
|
||||
}
|
||||
}
|
||||
return n, i, true
|
||||
}
|
||||
|
||||
// Parse IPv4 address (d.d.d.d).
|
||||
func ParseIPv4(s string) *[]byte {
|
||||
var p [IPv4len]byte;
|
||||
|
|
@ -307,7 +259,7 @@ func ParseIPv4(s string) *[]byte {
|
|||
n int;
|
||||
ok bool
|
||||
)
|
||||
n, i, ok = dtoi(s, i);
|
||||
n, i, ok = Dtoi(s, i);
|
||||
if !ok || n > 0xFF {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -346,7 +298,7 @@ func ParseIPv6(s string) *[]byte {
|
|||
j := 0;
|
||||
L: for j < IPv6len {
|
||||
// Hex number.
|
||||
n, i1, ok := xtoi(s, i);
|
||||
n, i1, ok := Xtoi(s, i);
|
||||
if !ok || n > 0xFFFF {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
53
src/lib/net/ip_test.go
Normal file
53
src/lib/net/ip_test.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net";
|
||||
"testing"
|
||||
)
|
||||
|
||||
func IPv4(a, b, c, d byte) *[]byte {
|
||||
return &[]byte{ 0,0,0,0, 0,0,0,0, 0,0,255,255, a,b,c,d }
|
||||
}
|
||||
|
||||
func Equal(a *[]byte, b *[]byte) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
if a == nil || b == nil || len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type ParseIPTest struct {
|
||||
in string;
|
||||
out *[]byte;
|
||||
}
|
||||
var parseiptests = []ParseIPTest {
|
||||
ParseIPTest{"127.0.1.2", IPv4(127, 0, 1, 2)},
|
||||
ParseIPTest{"127.0.0.1", IPv4(127, 0, 0, 1)},
|
||||
ParseIPTest{"127.0.0.256", nil},
|
||||
ParseIPTest{"abc", nil},
|
||||
ParseIPTest{"::ffff:127.0.0.1", IPv4(127, 0, 0, 1)},
|
||||
ParseIPTest{"2001:4860:0:2001::68",
|
||||
&[]byte{0x20,0x01, 0x48,0x60, 0,0, 0x20,0x01, 0,0, 0,0, 0,0, 0x00,0x68}},
|
||||
ParseIPTest{"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)},
|
||||
}
|
||||
|
||||
export func TestParseIP(t *testing.T) {
|
||||
for i := 0; i < len(parseiptests); i++ {
|
||||
tt := parseiptests[i];
|
||||
if out := ParseIP(tt.in); !Equal(out, tt.out) {
|
||||
t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,10 +16,13 @@ export var (
|
|||
MissingAddress = os.NewError("missing address");
|
||||
UnknownNetwork = os.NewError("unknown network");
|
||||
UnknownHost = os.NewError("unknown host");
|
||||
DNS_Error = os.NewError("dns error looking up host");
|
||||
UnknownPort = os.NewError("unknown port");
|
||||
UnknownSocketFamily = os.NewError("unknown socket family");
|
||||
)
|
||||
|
||||
export func LookupHost(name string) (name1 string, addrs *[]string, err *os.Error)
|
||||
|
||||
// Split "host:port" into "host" and "port".
|
||||
// Host cannot contain colons unless it is bracketed.
|
||||
func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
||||
|
|
@ -42,10 +45,8 @@ func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
|||
host = host[1:len(host)-1]
|
||||
} else {
|
||||
// ... but if there are no brackets, no colons.
|
||||
for i := 0; i < len(host); i++ {
|
||||
if host[i] == ':' {
|
||||
return "", "", BadAddress
|
||||
}
|
||||
if ByteIndex(host, ':') >= 0 {
|
||||
return "", "", BadAddress
|
||||
}
|
||||
}
|
||||
return host, port, nil
|
||||
|
|
@ -55,28 +56,12 @@ func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
|||
// If host contains colons, will join into "[host]:port".
|
||||
func JoinHostPort(host, port string) string {
|
||||
// If host has colons, have to bracket it.
|
||||
for i := 0; i < len(host); i++ {
|
||||
if host[i] == ':' {
|
||||
return "[" + host + "]:" + port
|
||||
}
|
||||
if ByteIndex(host, ':') >= 0 {
|
||||
return "[" + host + "]:" + port
|
||||
}
|
||||
return host + ":" + port
|
||||
}
|
||||
|
||||
func xdtoi(s string) (n int, ok bool) {
|
||||
if s == "" || s[0] < '0' || s[0] > '9' {
|
||||
return 0, false
|
||||
}
|
||||
n = 0;
|
||||
for i := 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
n = n*10 + int(s[i] - '0');
|
||||
if n >= 1000000 { // bigger than we need
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
// Convert "host:port" into IP address and port.
|
||||
// For now, host and port must be numeric literals.
|
||||
// Eventually, we'll have name resolution.
|
||||
|
|
@ -87,18 +72,33 @@ func HostPortToIP(net string, hostport string) (ip *[]byte, iport int, err *os.E
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
// TODO: Resolve host.
|
||||
|
||||
// Try as an IP address.
|
||||
addr := ParseIP(host);
|
||||
if addr == nil {
|
||||
return nil, 0, UnknownHost
|
||||
// Not an IP address. Try as a DNS name.
|
||||
hostname, addrs, err := LookupHost(host);
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, 0, UnknownHost
|
||||
}
|
||||
addr = ParseIP(addrs[0]);
|
||||
if addr == nil {
|
||||
// should not happen
|
||||
return nil, 0, BadAddress
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Resolve port.
|
||||
|
||||
p, ok := xdtoi(port);
|
||||
if !ok || p < 0 || p > 0xFFFF {
|
||||
return nil, 0, UnknownPort
|
||||
p, i, ok := Dtoi(port, 0);
|
||||
if !ok || i != len(port) {
|
||||
p, ok = LookupPort(net, port);
|
||||
if !ok {
|
||||
return nil, 0, UnknownPort
|
||||
}
|
||||
}
|
||||
if p < 0 || p > 0xFFFF {
|
||||
return nil, 0, BadAddress
|
||||
}
|
||||
|
||||
return addr, p, nil
|
||||
|
|
@ -284,13 +284,7 @@ func InternetSocket(net, laddr, raddr string, proto int64) (fd *FD, err *os.Erro
|
|||
var lip, rip *[]byte;
|
||||
var lport, rport int;
|
||||
var lerr, rerr *os.Error;
|
||||
// BUG 6g doesn't zero var lists
|
||||
lip = nil;
|
||||
rip = nil;
|
||||
lport = 0;
|
||||
rport = 0;
|
||||
lerr = nil;
|
||||
rerr = nil;
|
||||
|
||||
if laddr != "" {
|
||||
lip, lport, lerr = HostPortToIP(net, laddr);
|
||||
if lerr != nil {
|
||||
|
|
@ -335,9 +329,6 @@ rerr = nil;
|
|||
}
|
||||
|
||||
var la, ra *syscall.Sockaddr;
|
||||
// BUG
|
||||
la = nil;
|
||||
ra = nil;
|
||||
if lip != nil {
|
||||
la, lerr = cvt(lip, lport);
|
||||
if lerr != nil {
|
||||
|
|
|
|||
156
src/lib/net/parse.go
Normal file
156
src/lib/net/parse.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Simple file i/o and string manipulation, to avoid
|
||||
// depending on strconv and bufio.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"io";
|
||||
"os";
|
||||
)
|
||||
|
||||
package type File struct {
|
||||
fd *os.FD;
|
||||
data *[]byte;
|
||||
}
|
||||
|
||||
func (f *File) Close() {
|
||||
f.fd.Close()
|
||||
}
|
||||
|
||||
func (f *File) GetLineFromData() (s string, ok bool) {
|
||||
data := f.data;
|
||||
for i := 0; i < len(data); i++ {
|
||||
if data[i] == '\n' {
|
||||
s = string(data[0:i]);
|
||||
ok = true;
|
||||
// move data
|
||||
i++;
|
||||
n := len(data) - i;
|
||||
for j := 0; j < n; j++ {
|
||||
data[j] = data[i+j];
|
||||
}
|
||||
f.data = data[0:n];
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) ReadLine() (s string, ok bool) {
|
||||
if s, ok = f.GetLineFromData(); ok {
|
||||
return
|
||||
}
|
||||
if len(f.data) < cap(f.data) {
|
||||
ln := len(f.data);
|
||||
n, err := io.Readn(f.fd, f.data[ln:cap(f.data)]);
|
||||
if n >= 0 {
|
||||
f.data = f.data[0:ln+n];
|
||||
}
|
||||
}
|
||||
s, ok = f.GetLineFromData();
|
||||
return
|
||||
}
|
||||
|
||||
package func Open(name string) *File {
|
||||
fd, err := os.Open(name, os.O_RDONLY, 0);
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &File{fd, new([]byte, 1024)[0:0]};
|
||||
}
|
||||
|
||||
package func ByteIndex(s string, c byte) int {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Count occurrences in s of any bytes in t.
|
||||
package func CountAnyByte(s string, t string) int {
|
||||
n := 0;
|
||||
for i := 0; i < len(s); i++ {
|
||||
if ByteIndex(t, s[i]) >= 0 {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Split s at any bytes in t.
|
||||
package func SplitAtBytes(s string, t string) *[]string {
|
||||
a := new([]string, 1+CountAnyByte(s, t));
|
||||
n := 0;
|
||||
last := 0;
|
||||
for i := 0; i < len(s); i++ {
|
||||
if ByteIndex(t, s[i]) >= 0 {
|
||||
if last < i {
|
||||
a[n] = string(s[last:i]);
|
||||
n++;
|
||||
}
|
||||
last = i+1;
|
||||
}
|
||||
}
|
||||
if last < len(s) {
|
||||
a[n] = string(s[last:len(s)]);
|
||||
n++;
|
||||
}
|
||||
return a[0:n];
|
||||
}
|
||||
|
||||
package func GetFields(s string) *[]string {
|
||||
return SplitAtBytes(s, " \r\t\n");
|
||||
}
|
||||
|
||||
// Bigger than we need, not too big to worry about overflow
|
||||
const Big = 0xFFFFFF
|
||||
|
||||
// Decimal to integer starting at &s[i0].
|
||||
// Returns number, new offset, success.
|
||||
package func Dtoi(s string, i0 int) (n int, i int, ok bool) {
|
||||
n = 0;
|
||||
for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
n = n*10 + int(s[i] - '0');
|
||||
if n >= Big {
|
||||
return 0, i, false
|
||||
}
|
||||
}
|
||||
if i == i0 {
|
||||
return 0, i, false
|
||||
}
|
||||
return n, i, true
|
||||
}
|
||||
|
||||
// Hexadecimal to integer starting at &s[i0].
|
||||
// Returns number, new offset, success.
|
||||
package func Xtoi(s string, i0 int) (n int, i int, ok bool) {
|
||||
n = 0;
|
||||
for i = i0; i < len(s); i++ {
|
||||
if '0' <= s[i] && s[i] <= '9' {
|
||||
n *= 16;
|
||||
n += int(s[i] - '0')
|
||||
} else if 'a' <= s[i] && s[i] <= 'f' {
|
||||
n *= 16;
|
||||
n += int(s[i] - 'a') + 10
|
||||
} else if 'A' <= s[i] && s[i] <= 'F' {
|
||||
n *= 16;
|
||||
n += int(s[i] -'A') + 10
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if n >= Big {
|
||||
return 0, i, false
|
||||
}
|
||||
}
|
||||
if i == i0 {
|
||||
return 0, i, false
|
||||
}
|
||||
return n, i, true
|
||||
}
|
||||
|
||||
46
src/lib/net/parse_test.go
Normal file
46
src/lib/net/parse_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"bufio";
|
||||
"net";
|
||||
"os";
|
||||
"testing";
|
||||
)
|
||||
|
||||
export func TestReadLine(t *testing.T) {
|
||||
filename := "/etc/services"; // a nice big file
|
||||
|
||||
fd, err := os.Open(filename, os.O_RDONLY, 0);
|
||||
if err != nil {
|
||||
t.Fatalf("open %s: %v", filename, err);
|
||||
}
|
||||
br, err1 := bufio.NewBufRead(fd);
|
||||
if err1 != nil {
|
||||
t.Fatalf("bufio.NewBufRead: %v", err1);
|
||||
}
|
||||
|
||||
file := Open(filename);
|
||||
if file == nil {
|
||||
t.Fatalf("net.Open(%s) = nil", filename);
|
||||
}
|
||||
|
||||
lineno := 1;
|
||||
byteno := 0;
|
||||
for {
|
||||
bline, berr := br.ReadLineString('\n', false);
|
||||
line, ok := file.ReadLine();
|
||||
if (berr != nil) != !ok || bline != line {
|
||||
t.Fatalf("%s:%d (#%d)\nbufio => %q, %v\nnet => %q, %v",
|
||||
filename, lineno, byteno, bline, berr, line, ok);
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
lineno++;
|
||||
byteno += len(line) + 1;
|
||||
}
|
||||
}
|
||||
68
src/lib/net/port.go
Normal file
68
src/lib/net/port.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Read system port mappings from /etc/services
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"io";
|
||||
"net";
|
||||
"once";
|
||||
"os";
|
||||
"strconv";
|
||||
)
|
||||
|
||||
var services *map[string] *map[string] int
|
||||
|
||||
func ReadServices() {
|
||||
services = new(map[string] *map[string] int);
|
||||
file := Open("/etc/services");
|
||||
for line, ok := file.ReadLine(); ok; line, ok = file.ReadLine() {
|
||||
// "http 80/tcp www www-http # World Wide Web HTTP"
|
||||
if i := ByteIndex(line, '#'); i >= 0 {
|
||||
line = line[0:i];
|
||||
}
|
||||
f := GetFields(line);
|
||||
if len(f) < 2 {
|
||||
continue;
|
||||
}
|
||||
portnet := f[1]; // "tcp/80"
|
||||
port, j, ok := Dtoi(portnet, 0);
|
||||
if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
|
||||
continue
|
||||
}
|
||||
netw := portnet[j+1:len(portnet)]; // "tcp"
|
||||
m, ok1 := services[netw];
|
||||
if !ok1 {
|
||||
m = new(map[string] int);
|
||||
services[netw] = m;
|
||||
}
|
||||
for i := 0; i < len(f); i++ {
|
||||
if i != 1 { // f[1] was port/net
|
||||
m[f[i]] = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.Close();
|
||||
}
|
||||
|
||||
export func LookupPort(netw, name string) (port int, ok bool) {
|
||||
once.Do(&ReadServices);
|
||||
|
||||
switch netw {
|
||||
case "tcp4", "tcp6":
|
||||
netw = "tcp";
|
||||
case "udp4", "udp6":
|
||||
netw = "udp";
|
||||
}
|
||||
|
||||
m, mok := services[netw];
|
||||
if !mok {
|
||||
return
|
||||
}
|
||||
port, ok = m[name];
|
||||
return
|
||||
}
|
||||
|
||||
59
src/lib/net/port_test.go
Normal file
59
src/lib/net/port_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net";
|
||||
"testing";
|
||||
)
|
||||
|
||||
type PortTest struct {
|
||||
netw string;
|
||||
name string;
|
||||
port int;
|
||||
ok bool;
|
||||
}
|
||||
|
||||
var porttests = []PortTest {
|
||||
PortTest{ "tcp", "echo", 7, true },
|
||||
PortTest{ "tcp", "discard", 9, true },
|
||||
PortTest{ "tcp", "systat", 11, true },
|
||||
PortTest{ "tcp", "daytime", 13, true },
|
||||
PortTest{ "tcp", "chargen", 19, true },
|
||||
PortTest{ "tcp", "ftp-data", 20, true },
|
||||
PortTest{ "tcp", "ftp", 21, true },
|
||||
PortTest{ "tcp", "ssh", 22, true },
|
||||
PortTest{ "tcp", "telnet", 23, true },
|
||||
PortTest{ "tcp", "smtp", 25, true },
|
||||
PortTest{ "tcp", "time", 37, true },
|
||||
PortTest{ "tcp", "domain", 53, true },
|
||||
PortTest{ "tcp", "gopher", 70, true },
|
||||
PortTest{ "tcp", "finger", 79, true },
|
||||
PortTest{ "tcp", "http", 80, true },
|
||||
|
||||
PortTest{ "udp", "echo", 7, true },
|
||||
PortTest{ "udp", "tacacs", 49, true },
|
||||
PortTest{ "udp", "tftp", 69, true },
|
||||
PortTest{ "udp", "bootpc", 68, true },
|
||||
PortTest{ "udp", "bootps", 67, true },
|
||||
PortTest{ "udp", "domain", 53, true },
|
||||
PortTest{ "udp", "ntp", 123, true },
|
||||
PortTest{ "udp", "snmp", 161, true },
|
||||
PortTest{ "udp", "syslog", 514, true },
|
||||
PortTest{ "udp", "nfs", 2049, true },
|
||||
|
||||
PortTest{ "--badnet--", "zzz", 0, false },
|
||||
PortTest{ "tcp", "--badport--", 0, false },
|
||||
}
|
||||
|
||||
export func TestLookupPort(t *testing.T) {
|
||||
for i := 0; i < len(porttests); i++ {
|
||||
tt := porttests[i];
|
||||
if port, ok := LookupPort(tt.netw, tt.name); port != tt.port || ok != tt.ok {
|
||||
t.Errorf("LookupPort(%q, %q) = %v, %v; want %v, %v",
|
||||
tt.netw, tt.name, port, ok, tt.port, tt.ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,25 +2,15 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// $G $F.go && $L $F.$A && ./$A.out
|
||||
package net
|
||||
|
||||
package main
|
||||
import (
|
||||
"os";
|
||||
"io";
|
||||
"net";
|
||||
"syscall"
|
||||
"testing";
|
||||
)
|
||||
|
||||
func StringToBuf(s string) *[]byte {
|
||||
l := len(s);
|
||||
b := new([]byte, l);
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = s[i];
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
func Echo(fd io.ReadWrite, done *chan<- int) {
|
||||
var buf [1024]byte;
|
||||
|
||||
|
|
@ -34,10 +24,10 @@ func Echo(fd io.ReadWrite, done *chan<- int) {
|
|||
done <- 1
|
||||
}
|
||||
|
||||
func Serve(network, addr string, listening, done *chan<- int) {
|
||||
func Serve(t *testing.T, network, addr string, listening, done *chan<- int) {
|
||||
l, err := net.Listen(network, addr);
|
||||
if err != nil {
|
||||
panic("listen: "+err.String());
|
||||
t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err);
|
||||
}
|
||||
listening <- 1;
|
||||
|
||||
|
|
@ -54,44 +44,41 @@ func Serve(network, addr string, listening, done *chan<- int) {
|
|||
done <- 1
|
||||
}
|
||||
|
||||
func Connect(network, addr string) {
|
||||
func Connect(t *testing.T, network, addr string) {
|
||||
fd, err := net.Dial(network, "", addr);
|
||||
if err != nil {
|
||||
panic("connect: "+err.String());
|
||||
t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err);
|
||||
}
|
||||
|
||||
b := StringToBuf("hello, world\n");
|
||||
b := io.StringBytes("hello, world\n");
|
||||
var b1 [100]byte;
|
||||
|
||||
n, errno := fd.Write(b);
|
||||
if n != len(b) {
|
||||
panic("syscall.write in connect");
|
||||
t.Fatalf("fd.Write(%q) = %d, %v", b, n, errno);
|
||||
}
|
||||
|
||||
n, errno = fd.Read(&b1);
|
||||
if n != len(b) {
|
||||
panic("syscall.read in connect");
|
||||
t.Fatalf("fd.Read() = %d, %v", n, errno);
|
||||
}
|
||||
|
||||
// os.Stdout.Write((&b1)[0:n]);
|
||||
fd.Close();
|
||||
}
|
||||
|
||||
func Test(network, listenaddr, dialaddr string) {
|
||||
// print("Test ", network, " ", listenaddr, " ", dialaddr, "\n");
|
||||
func DoTest(t *testing.T, network, listenaddr, dialaddr string) {
|
||||
t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr);
|
||||
listening := new(chan int);
|
||||
done := new(chan int);
|
||||
go Serve(network, listenaddr, listening, done);
|
||||
go Serve(t, network, listenaddr, listening, done);
|
||||
<-listening; // wait for server to start
|
||||
Connect(network, dialaddr);
|
||||
Connect(t, network, dialaddr);
|
||||
<-done; // make sure server stopped
|
||||
}
|
||||
|
||||
func main() {
|
||||
Test("tcp", "0.0.0.0:9999", "127.0.0.1:9999");
|
||||
Test("tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
|
||||
Test("tcp", "[::]:9999", "127.0.0.1:9999");
|
||||
Test("tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
|
||||
sys.exit(0); // supposed to happen on return, doesn't
|
||||
export func TestTcpServer(t *testing.T) {
|
||||
DoTest(t, "tcp", "0.0.0.0:9999", "127.0.0.1:9999");
|
||||
DoTest(t, "tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
|
||||
DoTest(t, "tcp", "[::]:9999", "127.0.0.1:9999");
|
||||
DoTest(t, "tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ maketest \
|
|||
lib/hash\
|
||||
lib/json\
|
||||
lib/math\
|
||||
lib/net\
|
||||
lib/reflect\
|
||||
lib/regexp\
|
||||
lib/strconv\
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// $G $F.go && $L $F.$A && ./$A.out
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net";
|
||||
"flag";
|
||||
"io";
|
||||
"os";
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
|
||||
var ipv6 = false
|
||||
var ipv6_flag = flag.Bool("ipv6", false, &ipv6, "assume ipv6 tunnel is present")
|
||||
|
||||
func StringToBuf(s string) *[]byte
|
||||
{
|
||||
l := len(s);
|
||||
b := new([]byte, l);
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = s[i];
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
func Readn(fd io.Read, buf *[]byte) (n int, err *os.Error) {
|
||||
n = 0;
|
||||
for n < len(buf) {
|
||||
nn, e := fd.Read(buf[n:len(buf)]);
|
||||
if nn > 0 {
|
||||
n += nn
|
||||
}
|
||||
if e != nil {
|
||||
return n, e
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
||||
// fd is already connected to www.google.com port 80.
|
||||
// Run an HTTP request to fetch the main page.
|
||||
func FetchGoogle(fd net.Conn) {
|
||||
req := StringToBuf("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
|
||||
n, errno := fd.Write(req);
|
||||
|
||||
buf := new([1000]byte);
|
||||
n, errno = Readn(fd, buf);
|
||||
|
||||
fd.Close();
|
||||
if n < 1000 {
|
||||
panic("short http read");
|
||||
}
|
||||
}
|
||||
|
||||
func TestDial(network, addr string) {
|
||||
fd, err := net.Dial(network, "", addr);
|
||||
if err != nil {
|
||||
panic("net.Dial ", network, " ", addr, ": ", err.String())
|
||||
}
|
||||
FetchGoogle(fd)
|
||||
}
|
||||
|
||||
func TestDialTCP(network, addr string) {
|
||||
fd, err := net.DialTCP(network, "", addr);
|
||||
if err != nil {
|
||||
panic("net.DialTCP ", network, " ", addr, ": ", err.String())
|
||||
}
|
||||
FetchGoogle(fd)
|
||||
}
|
||||
|
||||
var addrs = []string {
|
||||
"74.125.19.99:80",
|
||||
"074.125.019.099:0080",
|
||||
"[::ffff:74.125.19.99]:80",
|
||||
"[::ffff:4a7d:1363]:80",
|
||||
"[0:0:0:0:0000:ffff:74.125.19.99]:80",
|
||||
"[0:0:0:0:000000:ffff:74.125.19.99]:80",
|
||||
"[0:0:0:0:0:ffff::74.125.19.99]:80",
|
||||
"[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set
|
||||
}
|
||||
|
||||
func main()
|
||||
{
|
||||
flag.Parse();
|
||||
// If no ipv6 tunnel, don't try the last address.
|
||||
if !ipv6 {
|
||||
addrs[len(addrs)-1] = ""
|
||||
}
|
||||
|
||||
for i := 0; i < len(addrs); i++ {
|
||||
addr := addrs[i];
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
// print(addr, "\n");
|
||||
TestDial("tcp", addr);
|
||||
TestDialTCP("tcp", addr);
|
||||
if addr[0] != '[' {
|
||||
TestDial("tcp4", addr);
|
||||
TestDialTCP("tcp4", addr)
|
||||
}
|
||||
TestDial("tcp6", addr);
|
||||
TestDialTCP("tcp6", addr)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue