Commit 83348f95 authored by Russ Cox's avatar Russ Cox

host and port name lookup

R=r,presotto
DELTA=1239  (935 added, 281 deleted, 23 changed)
OCL=21041
CL=21539
parent e29ce175
...@@ -3,7 +3,8 @@ ...@@ -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 ...@@ -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
......
...@@ -2,80 +2,60 @@ ...@@ -2,80 +2,60 @@
// 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 (
"net"; "net";
"flag"; "flag";
"io"; "io";
"os"; "os";
"syscall" "testing";
) )
// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address. // If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
var ipv6 = false var ipv6 = false
var ipv6_flag = flag.Bool("ipv6", false, &ipv6, "assume ipv6 tunnel is present") 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. // fd is already connected to www.google.com port 80.
// Run an HTTP request to fetch the main page. // Run an HTTP request to fetch the main page.
func FetchGoogle(fd net.Conn) { func FetchGoogle(t *testing.T, fd net.Conn, network, addr string) {
req := StringToBuf("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n"); req := io.StringBytes("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
n, errno := fd.Write(req); n, errno := fd.Write(req);
buf := new([1000]byte); buf := new([1000]byte);
n, errno = Readn(fd, buf); n, errno = io.Readn(fd, buf);
fd.Close();
if n < 1000 { if n < 1000 {
panic("short http read"); t.Errorf("FetchGoogle: short HTTP read from %s %s", network, addr);
return
} }
} }
func TestDial(network, addr string) { func DoDial(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("net.Dial ", network, " ", addr, ": ", err.String()) t.Errorf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err);
return
} }
FetchGoogle(fd) FetchGoogle(t, fd, network, addr);
fd.Close()
} }
func TestDialTCP(network, addr string) { func DoDialTCP(t *testing.T, network, addr string) {
fd, err := net.DialTCP(network, "", addr); fd, err := net.DialTCP(network, "", addr);
if err != nil { if err != nil {
panic("net.DialTCP ", network, " ", addr, ": ", err.String()) t.Errorf("net.DialTCP(%q, %q, %q) = _, %v", network, "", addr, err);
} else {
FetchGoogle(t, fd, network, addr);
} }
FetchGoogle(fd) fd.Close()
} }
var addrs = []string { var googleaddrs = []string {
"74.125.19.99:80", "74.125.19.99:80",
"www.google.com:80",
"74.125.19.99:http",
"www.google.com:http",
"074.125.019.099:0080", "074.125.019.099:0080",
"[::ffff:74.125.19.99]:80", "[::ffff:74.125.19.99]:80",
"[::ffff:4a7d:1363]:80", "[::ffff:4a7d:1363]:80",
...@@ -85,27 +65,25 @@ var addrs = []string { ...@@ -85,27 +65,25 @@ var addrs = []string {
"[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set "[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set
} }
func main() export func TestDialGoogle(t *testing.T) {
{
flag.Parse();
// If no ipv6 tunnel, don't try the last address. // If no ipv6 tunnel, don't try the last address.
if !ipv6 { if !ipv6 {
addrs[len(addrs)-1] = "" googleaddrs[len(googleaddrs)-1] = ""
} }
for i := 0; i < len(addrs); i++ { for i := 0; i < len(googleaddrs); i++ {
addr := addrs[i]; addr := googleaddrs[i];
if addr == "" { if addr == "" {
continue continue
} }
// print(addr, "\n"); t.Logf("-- %s --", addr);
TestDial("tcp", addr); DoDial(t, "tcp", addr);
TestDialTCP("tcp", addr); DoDialTCP(t, "tcp", addr);
if addr[0] != '[' { if addr[0] != '[' {
TestDial("tcp4", addr); DoDial(t, "tcp4", addr);
TestDialTCP("tcp4", addr) DoDialTCP(t, "tcp4", addr)
} }
TestDial("tcp6", addr); DoDial(t, "tcp6", addr);
TestDialTCP("tcp6", addr) DoDialTCP(t, "tcp6", addr)
} }
} }
// 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
}
// 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 ( ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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
} }
......
// 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 ( ...@@ -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) { ...@@ -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) { ...@@ -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 ...@@ -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 ...@@ -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; ...@@ -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 {
......
// 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
}
// 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;
}
}
// 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
}
// 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 @@ ...@@ -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) { ...@@ -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) { ...@@ -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 \ ...@@ -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\
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment