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.
|
# license that can be found in the LICENSE file.
|
||||||
|
|
||||||
# DO NOT EDIT. Automatically generated by gobuild.
|
# 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
|
O=6
|
||||||
GC=$(O)g
|
GC=$(O)g
|
||||||
CC=$(O)c -w
|
CC=$(O)c -w
|
||||||
|
|
@ -32,37 +33,55 @@ coverage: packages
|
||||||
$(AS) $*.s
|
$(AS) $*.s
|
||||||
|
|
||||||
O1=\
|
O1=\
|
||||||
fd_$(GOOS).$O\
|
|
||||||
ip.$O\
|
|
||||||
dnsmsg.$O\
|
dnsmsg.$O\
|
||||||
|
fd_$(GOOS).$O\
|
||||||
|
parse.$O\
|
||||||
|
|
||||||
O2=\
|
O2=\
|
||||||
fd.$O\
|
fd.$O\
|
||||||
net_$(GOOS).$O\
|
ip.$O\
|
||||||
|
port.$O\
|
||||||
|
|
||||||
O3=\
|
O3=\
|
||||||
|
dnsconfig.$O\
|
||||||
|
net_$(GOOS).$O\
|
||||||
|
|
||||||
|
O4=\
|
||||||
net.$O\
|
net.$O\
|
||||||
|
|
||||||
net.a: a1 a2 a3
|
O5=\
|
||||||
|
dnsclient.$O\
|
||||||
|
|
||||||
|
net.a: a1 a2 a3 a4 a5
|
||||||
|
|
||||||
a1: $(O1)
|
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)
|
rm -f $(O1)
|
||||||
|
|
||||||
a2: $(O2)
|
a2: $(O2)
|
||||||
$(AR) grc net.a fd.$O net_$(GOOS).$O
|
$(AR) grc net.a fd.$O ip.$O port.$O
|
||||||
rm -f $(O2)
|
rm -f $(O2)
|
||||||
|
|
||||||
a3: $(O3)
|
a3: $(O3)
|
||||||
$(AR) grc net.a net.$O
|
$(AR) grc net.a dnsconfig.$O net_$(GOOS).$O
|
||||||
rm -f $(O3)
|
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
|
newpkg: clean
|
||||||
$(AR) grc net.a
|
$(AR) grc net.a
|
||||||
|
|
||||||
$(O1): newpkg
|
$(O1): newpkg
|
||||||
$(O2): a1
|
$(O2): a1
|
||||||
$(O3): a2
|
$(O3): a2
|
||||||
|
$(O4): a3
|
||||||
|
$(O5): a4
|
||||||
|
|
||||||
nuke: clean
|
nuke: clean
|
||||||
rm -f $(GOROOT)/pkg/net.a
|
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_ClassCHAOS = 3;
|
||||||
DNS_ClassHESIOD = 4;
|
DNS_ClassHESIOD = 4;
|
||||||
DNS_ClassANY = 255;
|
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.
|
// The wire format for the DNS packet header.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
export const (
|
export const (
|
||||||
IPv4len = 4;
|
IPv4len = 4;
|
||||||
IPv6len = 16
|
IPv6len = 16
|
||||||
|
|
@ -240,58 +244,6 @@ export func MaskToString(mask *[]byte) string {
|
||||||
return IPToString(mask)
|
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).
|
// Parse IPv4 address (d.d.d.d).
|
||||||
func ParseIPv4(s string) *[]byte {
|
func ParseIPv4(s string) *[]byte {
|
||||||
var p [IPv4len]byte;
|
var p [IPv4len]byte;
|
||||||
|
|
@ -307,7 +259,7 @@ func ParseIPv4(s string) *[]byte {
|
||||||
n int;
|
n int;
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
n, i, ok = dtoi(s, i);
|
n, i, ok = Dtoi(s, i);
|
||||||
if !ok || n > 0xFF {
|
if !ok || n > 0xFF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +298,7 @@ func ParseIPv6(s string) *[]byte {
|
||||||
j := 0;
|
j := 0;
|
||||||
L: for j < IPv6len {
|
L: for j < IPv6len {
|
||||||
// Hex number.
|
// Hex number.
|
||||||
n, i1, ok := xtoi(s, i);
|
n, i1, ok := Xtoi(s, i);
|
||||||
if !ok || n > 0xFFFF {
|
if !ok || n > 0xFFFF {
|
||||||
return nil
|
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");
|
MissingAddress = os.NewError("missing address");
|
||||||
UnknownNetwork = os.NewError("unknown network");
|
UnknownNetwork = os.NewError("unknown network");
|
||||||
UnknownHost = os.NewError("unknown host");
|
UnknownHost = os.NewError("unknown host");
|
||||||
|
DNS_Error = os.NewError("dns error looking up host");
|
||||||
UnknownPort = os.NewError("unknown port");
|
UnknownPort = os.NewError("unknown port");
|
||||||
UnknownSocketFamily = os.NewError("unknown socket family");
|
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".
|
// Split "host:port" into "host" and "port".
|
||||||
// Host cannot contain colons unless it is bracketed.
|
// Host cannot contain colons unless it is bracketed.
|
||||||
func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
||||||
|
|
@ -42,12 +45,10 @@ func SplitHostPort(hostport string) (host, port string, err *os.Error) {
|
||||||
host = host[1:len(host)-1]
|
host = host[1:len(host)-1]
|
||||||
} else {
|
} else {
|
||||||
// ... but if there are no brackets, no colons.
|
// ... but if there are no brackets, no colons.
|
||||||
for i := 0; i < len(host); i++ {
|
if ByteIndex(host, ':') >= 0 {
|
||||||
if host[i] == ':' {
|
|
||||||
return "", "", BadAddress
|
return "", "", BadAddress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return host, port, nil
|
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".
|
// If host contains colons, will join into "[host]:port".
|
||||||
func JoinHostPort(host, port string) string {
|
func JoinHostPort(host, port string) string {
|
||||||
// If host has colons, have to bracket it.
|
// If host has colons, have to bracket it.
|
||||||
for i := 0; i < len(host); i++ {
|
if ByteIndex(host, ':') >= 0 {
|
||||||
if host[i] == ':' {
|
|
||||||
return "[" + host + "]:" + port
|
return "[" + host + "]:" + port
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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.
|
// Convert "host:port" into IP address and port.
|
||||||
// For now, host and port must be numeric literals.
|
// For now, host and port must be numeric literals.
|
||||||
// Eventually, we'll have name resolution.
|
// Eventually, we'll have name resolution.
|
||||||
|
|
@ -87,19 +72,34 @@ func HostPortToIP(net string, hostport string) (ip *[]byte, iport int, err *os.E
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Resolve host.
|
// Try as an IP address.
|
||||||
|
|
||||||
addr := ParseIP(host);
|
addr := ParseIP(host);
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
|
// 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
|
return nil, 0, UnknownHost
|
||||||
}
|
}
|
||||||
|
addr = ParseIP(addrs[0]);
|
||||||
|
if addr == nil {
|
||||||
|
// should not happen
|
||||||
|
return nil, 0, BadAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Resolve port.
|
p, i, ok := Dtoi(port, 0);
|
||||||
|
if !ok || i != len(port) {
|
||||||
p, ok := xdtoi(port);
|
p, ok = LookupPort(net, port);
|
||||||
if !ok || p < 0 || p > 0xFFFF {
|
if !ok {
|
||||||
return nil, 0, UnknownPort
|
return nil, 0, UnknownPort
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if p < 0 || p > 0xFFFF {
|
||||||
|
return nil, 0, BadAddress
|
||||||
|
}
|
||||||
|
|
||||||
return addr, p, nil
|
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 lip, rip *[]byte;
|
||||||
var lport, rport int;
|
var lport, rport int;
|
||||||
var lerr, rerr *os.Error;
|
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 != "" {
|
if laddr != "" {
|
||||||
lip, lport, lerr = HostPortToIP(net, laddr);
|
lip, lport, lerr = HostPortToIP(net, laddr);
|
||||||
if lerr != nil {
|
if lerr != nil {
|
||||||
|
|
@ -335,9 +329,6 @@ rerr = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var la, ra *syscall.Sockaddr;
|
var la, ra *syscall.Sockaddr;
|
||||||
// BUG
|
|
||||||
la = nil;
|
|
||||||
ra = nil;
|
|
||||||
if lip != nil {
|
if lip != nil {
|
||||||
la, lerr = cvt(lip, lport);
|
la, lerr = cvt(lip, lport);
|
||||||
if lerr != nil {
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// $G $F.go && $L $F.$A && ./$A.out
|
package net
|
||||||
|
|
||||||
package main
|
|
||||||
import (
|
import (
|
||||||
"os";
|
"os";
|
||||||
"io";
|
"io";
|
||||||
"net";
|
"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) {
|
func Echo(fd io.ReadWrite, done *chan<- int) {
|
||||||
var buf [1024]byte;
|
var buf [1024]byte;
|
||||||
|
|
||||||
|
|
@ -34,10 +24,10 @@ func Echo(fd io.ReadWrite, done *chan<- int) {
|
||||||
done <- 1
|
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);
|
l, err := net.Listen(network, addr);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("listen: "+err.String());
|
t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err);
|
||||||
}
|
}
|
||||||
listening <- 1;
|
listening <- 1;
|
||||||
|
|
||||||
|
|
@ -54,44 +44,41 @@ func Serve(network, addr string, listening, done *chan<- int) {
|
||||||
done <- 1
|
done <- 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func Connect(network, addr string) {
|
func Connect(t *testing.T, network, addr string) {
|
||||||
fd, err := net.Dial(network, "", addr);
|
fd, err := net.Dial(network, "", addr);
|
||||||
if err != nil {
|
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;
|
var b1 [100]byte;
|
||||||
|
|
||||||
n, errno := fd.Write(b);
|
n, errno := fd.Write(b);
|
||||||
if n != len(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);
|
n, errno = fd.Read(&b1);
|
||||||
if n != len(b) {
|
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();
|
fd.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test(network, listenaddr, dialaddr string) {
|
func DoTest(t *testing.T, network, listenaddr, dialaddr string) {
|
||||||
// print("Test ", network, " ", listenaddr, " ", dialaddr, "\n");
|
t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr);
|
||||||
listening := new(chan int);
|
listening := new(chan int);
|
||||||
done := 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
|
<-listening; // wait for server to start
|
||||||
Connect(network, dialaddr);
|
Connect(t, network, dialaddr);
|
||||||
<-done; // make sure server stopped
|
<-done; // make sure server stopped
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
export func TestTcpServer(t *testing.T) {
|
||||||
Test("tcp", "0.0.0.0:9999", "127.0.0.1:9999");
|
DoTest(t, "tcp", "0.0.0.0:9999", "127.0.0.1:9999");
|
||||||
Test("tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
|
DoTest(t, "tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
|
||||||
Test("tcp", "[::]:9999", "127.0.0.1:9999");
|
DoTest(t, "tcp", "[::]:9999", "127.0.0.1:9999");
|
||||||
Test("tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
|
DoTest(t, "tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
|
||||||
sys.exit(0); // supposed to happen on return, doesn't
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,6 +28,7 @@ maketest \
|
||||||
lib/hash\
|
lib/hash\
|
||||||
lib/json\
|
lib/json\
|
||||||
lib/math\
|
lib/math\
|
||||||
|
lib/net\
|
||||||
lib/reflect\
|
lib/reflect\
|
||||||
lib/regexp\
|
lib/regexp\
|
||||||
lib/strconv\
|
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