summaryrefslogtreecommitdiff
path: root/src/pkg/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net')
-rw-r--r--src/pkg/net/Makefile96
-rw-r--r--src/pkg/net/dialgoogle_test.go100
-rw-r--r--src/pkg/net/dnsclient.go227
-rw-r--r--src/pkg/net/dnsconfig.go113
-rw-r--r--src/pkg/net/dnsmsg.go679
-rw-r--r--src/pkg/net/fd.go429
-rw-r--r--src/pkg/net/fd_darwin.go115
-rw-r--r--src/pkg/net/fd_linux.go150
-rw-r--r--src/pkg/net/ip.go421
-rw-r--r--src/pkg/net/ip_test.go50
-rw-r--r--src/pkg/net/net.go862
-rw-r--r--src/pkg/net/parse.go160
-rw-r--r--src/pkg/net/parse_test.go44
-rw-r--r--src/pkg/net/port.go77
-rw-r--r--src/pkg/net/port_test.go59
-rw-r--r--src/pkg/net/server_test.go93
-rw-r--r--src/pkg/net/timeout_test.go42
17 files changed, 3717 insertions, 0 deletions
diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile
new file mode 100644
index 000000000..61c872089
--- /dev/null
+++ b/src/pkg/net/Makefile
@@ -0,0 +1,96 @@
+# 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.
+
+# DO NOT EDIT. Automatically generated by gobuild.
+# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_${GOOS}.go ip.go net.go parse.go port.go >Makefile
+
+D=
+
+include $(GOROOT)/src/Make.$(GOARCH)
+AR=gopack
+
+default: packages
+
+clean:
+ rm -rf *.[$(OS)] *.a [$(OS)].out _obj
+
+test: packages
+ gotest
+
+coverage: packages
+ gotest
+ 6cov -g `pwd` | grep -v '_test\.go:'
+
+%.$O: %.go
+ $(GC) -I_obj $*.go
+
+%.$O: %.c
+ $(CC) $*.c
+
+%.$O: %.s
+ $(AS) $*.s
+
+O1=\
+ dnsmsg.$O\
+ parse.$O\
+
+O2=\
+ fd_$(GOOS).$O\
+ ip.$O\
+ port.$O\
+
+O3=\
+ dnsconfig.$O\
+ fd.$O\
+
+O4=\
+ net.$O\
+
+O5=\
+ dnsclient.$O\
+
+
+phases: a1 a2 a3 a4 a5
+_obj$D/net.a: phases
+
+a1: $(O1)
+ $(AR) grc _obj$D/net.a dnsmsg.$O parse.$O
+ rm -f $(O1)
+
+a2: $(O2)
+ $(AR) grc _obj$D/net.a fd_$(GOOS).$O ip.$O port.$O
+ rm -f $(O2)
+
+a3: $(O3)
+ $(AR) grc _obj$D/net.a dnsconfig.$O fd.$O
+ rm -f $(O3)
+
+a4: $(O4)
+ $(AR) grc _obj$D/net.a net.$O
+ rm -f $(O4)
+
+a5: $(O5)
+ $(AR) grc _obj$D/net.a dnsclient.$O
+ rm -f $(O5)
+
+
+newpkg: clean
+ mkdir -p _obj$D
+ $(AR) grc _obj$D/net.a
+
+$(O1): newpkg
+$(O2): a1
+$(O3): a2
+$(O4): a3
+$(O5): a4
+$(O6): a5
+
+nuke: clean
+ rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/net.a
+
+packages: _obj$D/net.a
+
+install: packages
+ test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D
+ cp _obj$D/net.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/net.a
diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go
new file mode 100644
index 000000000..1e0c0aaf0
--- /dev/null
+++ b/src/pkg/net/dialgoogle_test.go
@@ -0,0 +1,100 @@
+// 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 (
+ "flag";
+ "fmt";
+ "io";
+ "net";
+ "os";
+ "syscall";
+ "testing";
+)
+
+// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
+var ipv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present")
+
+// fd is already connected to the destination, port 80.
+// Run an HTTP request to fetch the appropriate page.
+func fetchGoogle(t *testing.T, fd net.Conn, network, addr string) {
+ req := io.StringBytes("GET /intl/en/privacy.html HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
+ n, err := fd.Write(req);
+
+ buf := make([]byte, 1000);
+ n, err = io.FullRead(fd, buf);
+
+ if n < 1000 {
+ t.Errorf("fetchGoogle: short HTTP read from %s %s - %v", network, addr, err);
+ 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
+}
+
+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);
+
+ if !preferIPv4 {
+ // make sure preferIPv4 flag works.
+ preferIPv4 = true;
+ syscall.SocketDisableIPv6 = true;
+ doDial(t, "tcp4", addr);
+ doDialTCP(t, "tcp4", addr);
+ syscall.SocketDisableIPv6 = false;
+ preferIPv4 = false;
+ }
+ }
+ doDial(t, "tcp6", addr);
+ doDialTCP(t, "tcp6", addr)
+ }
+}
diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go
new file mode 100644
index 000000000..cfd67eabe
--- /dev/null
+++ b/src/pkg/net/dnsclient.go
@@ -0,0 +1,227 @@
+// 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";
+)
+
+// DNS errors returned by LookupHost.
+type DNSError struct {
+ os.ErrorString
+}
+var (
+ DNS_InternalError os.Error = &DNSError{"internal dns error"};
+ DNS_MissingConfig os.Error = &DNSError{"no dns configuration"};
+ DNS_No_Answer os.Error = &DNSError{"dns got no answer"};
+ DNS_BadRequest os.Error = &DNSError{"malformed dns request"};
+ DNS_BadReply os.Error = &DNSError{"malformed dns reply"};
+ DNS_ServerFailure os.Error = &DNSError{"dns server failure"};
+ DNS_NoServers os.Error = &DNSError{"no dns servers"};
+ DNS_NameTooLong os.Error = &DNSError{"dns name too long"};
+ DNS_RedirectLoop os.Error = &DNSError{"dns redirect loop"};
+ DNS_NameNotFound os.Error = &DNSError{"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
+ }
+
+ c.SetReadTimeout(1e9); // nanoseconds
+
+ buf := make([]byte, 2000); // More than enough.
+ n, err = c.Read(buf);
+ if err == os.EAGAIN {
+ continue;
+ }
+ if err != nil {
+ return nil, err;
+ }
+ buf = buf[0:n];
+ in := new(_DNS_Msg);
+ if !in.Unpack(buf) || in.id != out.id {
+ continue
+ }
+ return in, nil
+ }
+ return nil, DNS_No_Answer
+}
+
+
+// Find answer for name in dns message.
+// On return, if err == nil, addrs != nil.
+// TODO(rsc): Maybe return []IP instead?
+func answer(name string, dns *_DNS_Msg) (addrs []string, err os.Error) {
+ addrs = make([]string, 0, len(dns.answer));
+
+ 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
+var dnserr os.Error
+
+func loadConfig() {
+ cfg, dnserr = _DNS_ReadConfig();
+}
+
+// LookupHost looks up the host name using the local DNS resolver.
+// It returns the canonical name for the host and an array of that
+// host's addresses.
+func LookupHost(name string) (cname 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 dnserr != nil || cfg == nil {
+ // better error than file not found.
+ 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
+}
diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig.go
new file mode 100644
index 000000000..e56d964f2
--- /dev/null
+++ b/src/pkg/net/dnsconfig.go
@@ -0,0 +1,113 @@
+// 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";
+)
+
+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
+}
+
+var _DNS_configError os.Error;
+
+// 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.
+func _DNS_ReadConfig() (*_DNS_Config, os.Error) {
+ // TODO(rsc): 6g won't let me say file, err :=
+ var file *file;
+ var err os.Error;
+ file, err = open("/etc/resolv.conf");
+ if err != nil {
+ return nil, err
+ }
+ conf := new(_DNS_Config);
+ conf.servers = make([]string, 3)[0:0]; // small, but the standard limit
+ conf.search = make([]string, 0);
+ conf.ndots = 1;
+ conf.timeout = 1;
+ conf.attempts = 1;
+ conf.rotate = false;
+ 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 len(ParseIP(name)) != 0 {
+ 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 = make([]string, 1);
+ conf.search[0] = f[1];
+ } else {
+ conf.search = make([]string, 0)
+ }
+
+ case "search": // set search path to given servers
+ conf.search = make([]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, nil
+}
+
diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go
new file mode 100644
index 000000000..d7a467fc6
--- /dev/null
+++ b/src/pkg/net/dnsmsg.go
@@ -0,0 +1,679 @@
+// 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 packet assembly.
+//
+// This is intended to support name resolution during net.Dial.
+// It doesn't have to be blazing fast.
+//
+// Rather than write the usual handful of routines to pack and
+// unpack every message that can appear on the wire, we use
+// reflection to write a generic pack/unpack for structs and then
+// use it. Thus, if in the future we need to define new message
+// structs, no new pack/unpack/printing code needs to be written.
+//
+// The first half of this file defines the DNS message formats.
+// The second half implements the conversion to and from wire format.
+// A few of the structure elements have string tags to aid the
+// generic pack/unpack routines.
+//
+// TODO(rsc) There are enough names defined in this file that they're all
+// prefixed with _DNS_. Perhaps put this in its own package later.
+
+package net
+
+import (
+ "fmt";
+ "os";
+ "reflect";
+)
+
+// Packet formats
+
+// Wire constants.
+const (
+ // valid _DNS_RR_Header.rrtype and _DNS_Question.qtype
+ _DNS_TypeA = 1;
+ _DNS_TypeNS = 2;
+ _DNS_TypeMD = 3;
+ _DNS_TypeMF = 4;
+ _DNS_TypeCNAME = 5;
+ _DNS_TypeSOA = 6;
+ _DNS_TypeMB = 7;
+ _DNS_TypeMG = 8;
+ _DNS_TypeMR = 9;
+ _DNS_TypeNULL = 10;
+ _DNS_TypeWKS = 11;
+ _DNS_TypePTR = 12;
+ _DNS_TypeHINFO = 13;
+ _DNS_TypeMINFO = 14;
+ _DNS_TypeMX = 15;
+ _DNS_TypeTXT = 16;
+
+ // valid _DNS_Question.qtype only
+ _DNS_TypeAXFR = 252;
+ _DNS_TypeMAILB = 253;
+ _DNS_TypeMAILA = 254;
+ _DNS_TypeALL = 255;
+
+ // valid _DNS_Question.qclass
+ _DNS_ClassINET = 1;
+ _DNS_ClassCSNET = 2;
+ _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.
+type __DNS_Header struct {
+ id uint16;
+ bits uint16;
+ qdcount, ancount, nscount, arcount uint16;
+}
+
+const (
+ // __DNS_Header.bits
+ _QR = 1<<15; // query/response (response=1)
+ _AA = 1<<10; // authoritative
+ _TC = 1<<9; // truncated
+ _RD = 1<<8; // recursion desired
+ _RA = 1<<7; // recursion available
+)
+
+// DNS queries.
+type _DNS_Question struct {
+ name string "domain-name"; // "domain-name" specifies encoding; see packers below
+ qtype uint16;
+ qclass uint16;
+}
+
+// DNS responses (resource records).
+// There are many types of messages,
+// but they all share the same header.
+type _DNS_RR_Header struct {
+ name string "domain-name";
+ rrtype uint16;
+ class uint16;
+ ttl uint32;
+ rdlength uint16; // length of data after header
+}
+
+func (h *_DNS_RR_Header) Header() *_DNS_RR_Header {
+ return h
+}
+
+type _DNS_RR interface {
+ Header() *_DNS_RR_Header
+}
+
+
+// Specific DNS RR formats for each query type.
+
+type _DNS_RR_CNAME struct {
+ _DNS_RR_Header;
+ cname string "domain-name";
+}
+
+type _DNS_RR_HINFO struct {
+ _DNS_RR_Header;
+ cpu string;
+ os string;
+}
+
+type _DNS_RR_MB struct {
+ _DNS_RR_Header;
+ mb string "domain-name";
+}
+
+type _DNS_RR_MG struct {
+ _DNS_RR_Header;
+ mg string "domain-name";
+}
+
+type _DNS_RR_MINFO struct {
+ _DNS_RR_Header;
+ rmail string "domain-name";
+ email string "domain-name";
+}
+
+type _DNS_RR_MR struct {
+ _DNS_RR_Header;
+ mr string "domain-name";
+}
+
+type _DNS_RR_MX struct {
+ _DNS_RR_Header;
+ pref uint16;
+ mx string "domain-name";
+}
+
+type _DNS_RR_NS struct {
+ _DNS_RR_Header;
+ ns string "domain-name";
+}
+
+type _DNS_RR_PTR struct {
+ _DNS_RR_Header;
+ ptr string "domain-name";
+}
+
+type _DNS_RR_SOA struct {
+ _DNS_RR_Header;
+ ns string "domain-name";
+ mbox string "domain-name";
+ serial uint32;
+ refresh uint32;
+ retry uint32;
+ expire uint32;
+ minttl uint32;
+}
+
+type _DNS_RR_TXT struct {
+ _DNS_RR_Header;
+ txt string; // not domain name
+}
+
+type _DNS_RR_A struct {
+ _DNS_RR_Header;
+ a uint32 "ipv4";
+}
+
+
+// Packing and unpacking.
+//
+// All the packers and unpackers take a (msg []byte, off int)
+// and return (off1 int, ok bool). If they return ok==false, they
+// also return off1==len(msg), so that the next unpacker will
+// also fail. This lets us avoid checks of ok until the end of a
+// packing sequence.
+
+// Map of constructors for each RR wire type.
+var rr_mk = map[int] func()_DNS_RR {
+ _DNS_TypeCNAME: func() _DNS_RR { return new(_DNS_RR_CNAME) },
+ _DNS_TypeHINFO: func() _DNS_RR { return new(_DNS_RR_HINFO) },
+ _DNS_TypeMB: func() _DNS_RR { return new(_DNS_RR_MB) },
+ _DNS_TypeMG: func() _DNS_RR { return new(_DNS_RR_MG) },
+ _DNS_TypeMINFO: func() _DNS_RR { return new(_DNS_RR_MINFO) },
+ _DNS_TypeMR: func() _DNS_RR { return new(_DNS_RR_MR) },
+ _DNS_TypeMX: func() _DNS_RR { return new(_DNS_RR_MX) },
+ _DNS_TypeNS: func() _DNS_RR { return new(_DNS_RR_NS) },
+ _DNS_TypePTR: func() _DNS_RR { return new(_DNS_RR_PTR) },
+ _DNS_TypeSOA: func() _DNS_RR { return new(_DNS_RR_SOA) },
+ _DNS_TypeTXT: func() _DNS_RR { return new(_DNS_RR_TXT) },
+ _DNS_TypeA: func() _DNS_RR { return new(_DNS_RR_A) },
+}
+
+// Pack a domain name s into msg[off:].
+// Domain names are a sequence of counted strings
+// split at the dots. They end with a zero-length string.
+func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) {
+ // Add trailing dot to canonicalize name.
+ if n := len(s); n == 0 || s[n-1] != '.' {
+ s += ".";
+ }
+
+ // Each dot ends a segment of the name.
+ // We trade each dot byte for a length byte.
+ // There is also a trailing zero.
+ // Check that we have all the space we need.
+ tot := len(s) + 1;
+ if off+tot > len(msg) {
+ return len(msg), false
+ }
+
+ // Emit sequence of counted strings, chopping at dots.
+ begin := 0;
+ for i := 0; i < len(s); i++ {
+ if s[i] == '.' {
+ if i - begin >= 1<<6 { // top two bits of length must be clear
+ return len(msg), false
+ }
+ msg[off] = byte(i - begin);
+ off++;
+ for j := begin; j < i; j++ {
+ msg[off] = s[j];
+ off++;
+ }
+ begin = i+1;
+ }
+ }
+ msg[off] = 0;
+ off++;
+ return off, true
+}
+
+// Unpack a domain name.
+// In addition to the simple sequences of counted strings above,
+// domain names are allowed to refer to strings elsewhere in the
+// packet, to avoid repeating common suffixes when returning
+// many entries in a single domain. The pointers are marked
+// by a length byte with the top two bits set. Ignoring those
+// two bits, that byte and the next give a 14 bit offset from msg[0]
+// where we should pick up the trail.
+// Note that if we jump elsewhere in the packet,
+// we return off1 == the offset after the first pointer we found,
+// which is where the next record will start.
+// In theory, the pointers are only allowed to jump backward.
+// We let them jump anywhere and stop jumping after a while.
+func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) {
+ s = "";
+ ptr := 0; // number of pointers followed
+Loop:
+ for {
+ if off >= len(msg) {
+ return "", len(msg), false
+ }
+ c := int(msg[off]);
+ off++;
+ switch c&0xC0 {
+ case 0x00:
+ if c == 0x00 {
+ // end of name
+ break Loop
+ }
+ // literal string
+ if off+c > len(msg) {
+ return "", len(msg), false
+ }
+ s += string(msg[off:off+c]) + ".";
+ off += c;
+ case 0xC0:
+ // pointer to somewhere else in msg.
+ // remember location after first ptr,
+ // since that's how many bytes we consumed.
+ // also, don't follow too many pointers --
+ // maybe there's a loop.
+ if off >= len(msg) {
+ return "", len(msg), false
+ }
+ c1 := msg[off];
+ off++;
+ if ptr == 0 {
+ off1 = off
+ }
+ if ptr++; ptr > 10 {
+ return "", len(msg), false
+ }
+ off = (c^0xC0)<<8 | int(c1);
+ default:
+ // 0x80 and 0x40 are reserved
+ return "", len(msg), false
+ }
+ }
+ if ptr == 0 {
+ off1 = off
+ }
+ return s, off1, true
+}
+
+// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
+// and other (often anonymous) structs.
+func packStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
+ for i := 0; i < val.Len(); i++ {
+ fld := val.Field(i);
+ name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i);
+ switch fld.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type());
+ return len(msg), false;
+ case reflect.StructKind:
+ off, ok = packStructValue(fld.(reflect.StructValue), msg, off);
+ case reflect.Uint16Kind:
+ i := fld.(reflect.Uint16Value).Get();
+ if off+2 > len(msg) {
+ return len(msg), false
+ }
+ msg[off] = byte(i>>8);
+ msg[off+1] = byte(i);
+ off += 2;
+ case reflect.Uint32Kind:
+ i := fld.(reflect.Uint32Value).Get();
+ if off+4 > len(msg) {
+ return len(msg), false
+ }
+ msg[off] = byte(i>>24);
+ msg[off+1] = byte(i>>16);
+ msg[off+2] = byte(i>>8);
+ msg[off+4] = byte(i);
+ off += 4;
+ case reflect.StringKind:
+ // There are multiple string encodings.
+ // The tag distinguishes ordinary strings from domain names.
+ s := fld.(reflect.StringValue).Get();
+ switch tag {
+ default:
+ fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag);
+ return len(msg), false;
+ case "domain-name":
+ off, ok = packDomainName(s, msg, off);
+ if !ok {
+ return len(msg), false
+ }
+ case "":
+ // Counted string: 1 byte length.
+ if len(s) > 255 || off + 1 + len(s) > len(msg) {
+ return len(msg), false
+ }
+ msg[off] = byte(len(s));
+ off++;
+ for i := 0; i < len(s); i++ {
+ msg[off+i] = s[i];
+ }
+ off += len(s);
+ }
+ }
+ }
+ return off, true
+}
+
+func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
+ val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue);
+ off, ok = packStructValue(val, msg, off);
+ return off, ok
+}
+
+// Unpack a reflect.StructValue from msg.
+// Same restrictions as packStructValue.
+func unpackStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
+ for i := 0; i < val.Len(); i++ {
+ name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i);
+ fld := val.Field(i);
+ switch fld.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type());
+ return len(msg), false;
+ case reflect.StructKind:
+ off, ok = unpackStructValue(fld.(reflect.StructValue), msg, off);
+ case reflect.Uint16Kind:
+ if off+2 > len(msg) {
+ return len(msg), false
+ }
+ i := uint16(msg[off])<<8 | uint16(msg[off+1]);
+ fld.(reflect.Uint16Value).Set(i);
+ off += 2;
+ case reflect.Uint32Kind:
+ if off+4 > len(msg) {
+ return len(msg), false
+ }
+ i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]);
+ fld.(reflect.Uint32Value).Set(i);
+ off += 4;
+ case reflect.StringKind:
+ var s string;
+ switch tag {
+ default:
+ fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag);
+ return len(msg), false;
+ case "domain-name":
+ s, off, ok = unpackDomainName(msg, off);
+ if !ok {
+ return len(msg), false
+ }
+ case "":
+ if off >= len(msg) || off+1+int(msg[off]) > len(msg) {
+ return len(msg), false
+ }
+ n := int(msg[off]);
+ off++;
+ b := make([]byte, n);
+ for i := 0; i < n; i++ {
+ b[i] = msg[off+i];
+ }
+ off += n;
+ s = string(b);
+ }
+ fld.(reflect.StringValue).Set(s);
+ }
+ }
+ return off, true
+}
+
+func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
+ val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue);
+ off, ok = unpackStructValue(val, msg, off);
+ return off, ok
+}
+
+// Generic struct printer.
+// Doesn't care about the string tag "domain-name",
+// but does look for an "ipv4" tag on uint32 variables,
+// printing them as IP addresses.
+func printStructValue(val reflect.StructValue) string {
+ s := "{";
+ for i := 0; i < val.Len(); i++ {
+ if i > 0 {
+ s += ", ";
+ }
+ name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i);
+ fld := val.Field(i);
+ if name != "" && name != "?" { // BUG? Shouldn't the reflect library hide "?" ?
+ s += name + "=";
+ }
+ kind := fld.Kind();
+ switch {
+ case kind == reflect.StructKind:
+ s += printStructValue(fld.(reflect.StructValue));
+ case kind == reflect.Uint32Kind && tag == "ipv4":
+ i := fld.(reflect.Uint32Value).Get();
+ s += fmt.Sprintf("%d.%d.%d.%d", (i>>24)&0xFF, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
+ default:
+ s += fmt.Sprint(fld.Interface())
+ }
+ }
+ s += "}";
+ return s;
+}
+
+func printStruct(any interface{}) string {
+ val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue);
+ s := printStructValue(val);
+ return s
+}
+
+// Resource record packer.
+func packRR(rr _DNS_RR, msg []byte, off int) (off2 int, ok bool) {
+ var off1 int;
+ // pack twice, once to find end of header
+ // and again to find end of packet.
+ // a bit inefficient but this doesn't need to be fast.
+ // off1 is end of header
+ // off2 is end of rr
+ off1, ok = packStruct(rr.Header(), msg, off);
+ off2, ok = packStruct(rr, msg, off);
+ if !ok {
+ return len(msg), false
+ }
+ // pack a third time; redo header with correct data length
+ rr.Header().rdlength = uint16(off2 - off1);
+ packStruct(rr.Header(), msg, off);
+ return off2, true
+}
+
+// Resource record unpacker.
+func unpackRR(msg []byte, off int) (rr _DNS_RR, off1 int, ok bool) {
+ // unpack just the header, to find the rr type and length
+ var h _DNS_RR_Header;
+ off0 := off;
+ if off, ok = unpackStruct(&h, msg, off); !ok {
+ return nil, len(msg), false
+ }
+ end := off+int(h.rdlength);
+
+ // make an rr of that type and re-unpack.
+ // again inefficient but doesn't need to be fast.
+ mk, known := rr_mk[int(h.rrtype)];
+ if !known {
+ return &h, end, true
+ }
+ rr = mk();
+ off, ok = unpackStruct(rr, msg, off0);
+ if off != end {
+ return &h, end, true
+ }
+ return rr, off, ok
+}
+
+// Usable representation of a DNS packet.
+
+// A manually-unpacked version of (id, bits).
+// This is in its own struct for easy printing.
+type __DNS_Msg_Top struct {
+ id uint16;
+ response bool;
+ opcode int;
+ authoritative bool;
+ truncated bool;
+ recursion_desired bool;
+ recursion_available bool;
+ rcode int;
+}
+
+type _DNS_Msg struct {
+ __DNS_Msg_Top;
+ question []_DNS_Question;
+ answer []_DNS_RR;
+ ns []_DNS_RR;
+ extra []_DNS_RR;
+}
+
+
+func (dns *_DNS_Msg) Pack() (msg []byte, ok bool) {
+ var dh __DNS_Header;
+
+ // Convert convenient _DNS_Msg into wire-like __DNS_Header.
+ dh.id = dns.id;
+ dh.bits = uint16(dns.opcode)<<11 | uint16(dns.rcode);
+ if dns.recursion_available {
+ dh.bits |= _RA;
+ }
+ if dns.recursion_desired {
+ dh.bits |= _RD;
+ }
+ if dns.truncated {
+ dh.bits |= _TC;
+ }
+ if dns.authoritative {
+ dh.bits |= _AA;
+ }
+ if dns.response {
+ dh.bits |= _QR;
+ }
+
+ // Prepare variable sized arrays.
+ question := dns.question;
+ answer := dns.answer;
+ ns := dns.ns;
+ extra := dns.extra;
+
+ dh.qdcount = uint16(len(question));
+ dh.ancount = uint16(len(answer));
+ dh.nscount = uint16(len(ns));
+ dh.arcount = uint16(len(extra));
+
+ // Could work harder to calculate message size,
+ // but this is far more than we need and not
+ // big enough to hurt the allocator.
+ msg = make([]byte, 2000);
+
+ // Pack it in: header and then the pieces.
+ off := 0;
+ off, ok = packStruct(&dh, msg, off);
+ for i := 0; i < len(question); i++ {
+ off, ok = packStruct(&question[i], msg, off);
+ }
+ for i := 0; i < len(answer); i++ {
+ off, ok = packStruct(answer[i], msg, off);
+ }
+ for i := 0; i < len(ns); i++ {
+ off, ok = packStruct(ns[i], msg, off);
+ }
+ for i := 0; i < len(extra); i++ {
+ off, ok = packStruct(extra[i], msg, off);
+ }
+ if !ok {
+ return nil, false
+ }
+ return msg[0:off], true
+}
+
+func (dns *_DNS_Msg) Unpack(msg []byte) bool {
+ // Header.
+ var dh __DNS_Header;
+ off := 0;
+ var ok bool;
+ if off, ok = unpackStruct(&dh, msg, off); !ok {
+ return false
+ }
+ dns.id = dh.id;
+ dns.response = (dh.bits & _QR) != 0;
+ dns.opcode = int(dh.bits >> 11) & 0xF;
+ dns.authoritative = (dh.bits & _AA) != 0;
+ dns.truncated = (dh.bits & _TC) != 0;
+ dns.recursion_desired = (dh.bits & _RD) != 0;
+ dns.recursion_available = (dh.bits & _RA) != 0;
+ dns.rcode = int(dh.bits & 0xF);
+
+ // Arrays.
+ dns.question = make([]_DNS_Question, dh.qdcount);
+ dns.answer = make([]_DNS_RR, dh.ancount);
+ dns.ns = make([]_DNS_RR, dh.nscount);
+ dns.extra = make([]_DNS_RR, dh.arcount);
+
+ for i := 0; i < len(dns.question); i++ {
+ off, ok = unpackStruct(&dns.question[i], msg, off);
+ }
+ for i := 0; i < len(dns.answer); i++ {
+ dns.answer[i], off, ok = unpackRR(msg, off);
+ }
+ for i := 0; i < len(dns.ns); i++ {
+ dns.ns[i], off, ok = unpackRR(msg, off);
+ }
+ for i := 0; i < len(dns.extra); i++ {
+ dns.extra[i], off, ok = unpackRR(msg, off);
+ }
+ if !ok {
+ return false
+ }
+// if off != len(msg) {
+// println("extra bytes in dns packet", off, "<", len(msg));
+// }
+ return true
+}
+
+func (dns *_DNS_Msg) String() string {
+ s := "DNS: "+printStruct(&dns.__DNS_Msg_Top)+"\n";
+ if len(dns.question) > 0 {
+ s += "-- Questions\n";
+ for i := 0; i < len(dns.question); i++ {
+ s += printStruct(&dns.question[i])+"\n";
+ }
+ }
+ if len(dns.answer) > 0 {
+ s += "-- Answers\n";
+ for i := 0; i < len(dns.answer); i++ {
+ s += printStruct(dns.answer[i])+"\n";
+ }
+ }
+ if len(dns.ns) > 0 {
+ s += "-- Name servers\n";
+ for i := 0; i < len(dns.ns); i++ {
+ s += printStruct(dns.ns[i])+"\n";
+ }
+ }
+ if len(dns.extra) > 0 {
+ s += "-- Extra\n";
+ for i := 0; i < len(dns.extra); i++ {
+ s += printStruct(dns.extra[i])+"\n";
+ }
+ }
+ return s;
+}
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
new file mode 100644
index 000000000..9404ed0bd
--- /dev/null
+++ b/src/pkg/net/fd.go
@@ -0,0 +1,429 @@
+// 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.
+
+// TODO(rsc): All the prints in this file should go to standard error.
+
+package net
+
+import (
+ "net";
+ "once";
+ "os";
+ "sync";
+ "syscall";
+)
+
+// Network file descriptor.
+type netFD struct {
+ // immutable until Close
+ fd int;
+ file *os.File;
+ cr chan *netFD;
+ cw chan *netFD;
+ net string;
+ laddr string;
+ raddr string;
+
+ // owned by client
+ rdeadline_delta int64;
+ rdeadline int64;
+ rio sync.Mutex;
+ wdeadline_delta int64;
+ wdeadline int64;
+ wio sync.Mutex;
+
+ // owned by fd wait server
+ ncr, ncw int;
+}
+
+// A pollServer helps FDs determine when to retry a non-blocking
+// read or write after they get EAGAIN. When an FD needs to wait,
+// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
+// request to the poll server. Then receive on fd.cr/fd.cw.
+// When the pollServer finds that i/o on FD should be possible
+// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
+// This protocol is implemented as s.WaitRead() and s.WaitWrite().
+//
+// There is one subtlety: when sending on s.cr/s.cw, the
+// poll server is probably in a system call, waiting for an fd
+// to become ready. It's not looking at the request channels.
+// To resolve this, the poll server waits not just on the FDs it has
+// been given but also its own pipe. After sending on the
+// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
+// byte to the pipe, causing the pollServer's poll system call to
+// return. In response to the pipe being readable, the pollServer
+// re-polls its request channels.
+//
+// Note that the ordering is "send request" and then "wake up server".
+// If the operations were reversed, there would be a race: the poll
+// server might wake up and look at the request channel, see that it
+// was empty, and go back to sleep, all before the requester managed
+// to send the request. Because the send must complete before the wakeup,
+// the request channel must be buffered. A buffer of size 1 is sufficient
+// for any request load. If many processes are trying to submit requests,
+// one will succeed, the pollServer will read the request, and then the
+// channel will be empty for the next process's request. A larger buffer
+// might help batch requests.
+
+type pollServer struct {
+ cr, cw chan *netFD; // buffered >= 1
+ pr, pw *os.File;
+ pending map[int] *netFD;
+ poll *pollster; // low-level OS hooks
+ deadline int64; // next deadline (nsec since 1970)
+}
+func (s *pollServer) Run();
+
+func newPollServer() (s *pollServer, err os.Error) {
+ s = new(pollServer);
+ s.cr = make(chan *netFD, 1);
+ s.cw = make(chan *netFD, 1);
+ if s.pr, s.pw, err = os.Pipe(); err != nil {
+ return nil, err
+ }
+ var e int;
+ if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 {
+ Errno:
+ err = os.ErrnoToError(e);
+ Error:
+ s.pr.Close();
+ s.pw.Close();
+ return nil, err;
+ }
+ if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 {
+ goto Errno;
+ }
+ if s.poll, err = newpollster(); err != nil {
+ goto Error;
+ }
+ if err = s.poll.AddFD(s.pr.Fd(), 'r', true); err != nil {
+ s.poll.Close();
+ goto Error
+ }
+ s.pending = make(map[int] *netFD);
+ go s.Run();
+ return s, nil
+}
+
+func (s *pollServer) AddFD(fd *netFD, mode int) {
+ // TODO(rsc): This check handles a race between
+ // one goroutine reading and another one closing,
+ // but it doesn't solve the race completely:
+ // it still could happen that one goroutine closes
+ // but we read fd.fd before it does, and then
+ // another goroutine creates a new open file with
+ // that fd, which we'd now be referring to.
+ // The fix is probably to send the Close call
+ // through the poll server too, except that
+ // not all Reads and Writes go through the poll
+ // server even now.
+ intfd := fd.fd;
+ if intfd < 0 {
+ // fd closed underfoot
+ if mode == 'r' {
+ fd.cr <- fd
+ } else {
+ fd.cw <- fd
+ }
+ return
+ }
+ if err := s.poll.AddFD(intfd, mode, false); err != nil {
+ panicln("pollServer AddFD ", intfd, ": ", err.String(), "\n");
+ return
+ }
+
+ var t int64;
+ key := intfd << 1;
+ if mode == 'r' {
+ fd.ncr++;
+ t = fd.rdeadline;
+ } else {
+ fd.ncw++;
+ key++;
+ t = fd.wdeadline;
+ }
+ s.pending[key] = fd;
+ if t > 0 && (s.deadline == 0 || t < s.deadline) {
+ s.deadline = t;
+ }
+}
+
+func (s *pollServer) LookupFD(fd int, mode int) *netFD {
+ key := fd << 1;
+ if mode == 'w' {
+ key++;
+ }
+ netfd, ok := s.pending[key];
+ if !ok {
+ return nil
+ }
+ s.pending[key] = nil, false;
+ return netfd
+}
+
+func (s *pollServer) WakeFD(fd *netFD, mode int) {
+ if mode == 'r' {
+ for fd.ncr > 0 {
+ fd.ncr--;
+ fd.cr <- fd
+ }
+ } else {
+ for fd.ncw > 0 {
+ fd.ncw--;
+ fd.cw <- fd
+ }
+ }
+}
+
+func (s *pollServer) Now() int64 {
+ sec, nsec, err := os.Time();
+ if err != nil {
+ panic("net: os.Time: ", err.String());
+ }
+ nsec += sec * 1e9;
+ return nsec;
+}
+
+func (s *pollServer) CheckDeadlines() {
+ now := s.Now();
+ // TODO(rsc): This will need to be handled more efficiently,
+ // probably with a heap indexed by wakeup time.
+
+ var next_deadline int64;
+ for key, fd := range s.pending {
+ var t int64;
+ var mode int;
+ if key&1 == 0 {
+ mode = 'r';
+ } else {
+ mode = 'w';
+ }
+ if mode == 'r' {
+ t = fd.rdeadline;
+ } else {
+ t = fd.wdeadline;
+ }
+ if t > 0 {
+ if t <= now {
+ s.pending[key] = nil, false;
+ if mode == 'r' {
+ s.poll.DelFD(fd.fd, mode);
+ fd.rdeadline = -1;
+ } else {
+ s.poll.DelFD(fd.fd, mode);
+ fd.wdeadline = -1;
+ }
+ s.WakeFD(fd, mode);
+ } else if next_deadline == 0 || t < next_deadline {
+ next_deadline = t;
+ }
+ }
+ }
+ s.deadline = next_deadline;
+}
+
+func (s *pollServer) Run() {
+ var scratch [100]byte;
+ for {
+ var t = s.deadline;
+ if t > 0 {
+ t = t - s.Now();
+ if t < 0 {
+ s.CheckDeadlines();
+ continue;
+ }
+ }
+ fd, mode, err := s.poll.WaitFD(t);
+ if err != nil {
+ print("pollServer WaitFD: ", err.String(), "\n");
+ return
+ }
+ if fd < 0 {
+ // Timeout happened.
+ s.CheckDeadlines();
+ continue;
+ }
+ if fd == s.pr.Fd() {
+ // Drain our wakeup pipe.
+ for nn, e := s.pr.Read(&scratch); nn > 0; {
+ nn, e = s.pr.Read(&scratch)
+ }
+
+ // Read from channels
+ for fd, ok := <-s.cr; ok; fd, ok = <-s.cr {
+ s.AddFD(fd, 'r')
+ }
+ for fd, ok := <-s.cw; ok; fd, ok = <-s.cw {
+ s.AddFD(fd, 'w')
+ }
+ } else {
+ netfd := s.LookupFD(fd, mode);
+ if netfd == nil {
+ print("pollServer: unexpected wakeup for fd=", netfd, " mode=", string(mode), "\n");
+ continue
+ }
+ s.WakeFD(netfd, mode);
+ }
+ }
+}
+
+var wakeupbuf [1]byte;
+func (s *pollServer) Wakeup() {
+ s.pw.Write(&wakeupbuf)
+}
+
+func (s *pollServer) WaitRead(fd *netFD) {
+ s.cr <- fd;
+ s.Wakeup();
+ <-fd.cr
+}
+
+func (s *pollServer) WaitWrite(fd *netFD) {
+ s.cw <- fd;
+ s.Wakeup();
+ <-fd.cw
+}
+
+
+// Network FD methods.
+// All the network FDs use a single pollServer.
+
+var pollserver *pollServer
+
+func _StartServer() {
+ p, err := newPollServer();
+ if err != nil {
+ print("Start pollServer: ", err.String(), "\n")
+ }
+ pollserver = p
+}
+
+func newFD(fd int, net, laddr, raddr string) (f *netFD, err os.Error) {
+ if pollserver == nil {
+ once.Do(_StartServer);
+ }
+ if e := syscall.SetNonblock(fd, true); e != 0 {
+ return nil, os.ErrnoToError(e);
+ }
+ f = new(netFD);
+ f.fd = fd;
+ f.net = net;
+ f.laddr = laddr;
+ f.raddr = raddr;
+ f.file = os.NewFile(fd, "net: " + net + " " + laddr + " " + raddr);
+ f.cr = make(chan *netFD, 1);
+ f.cw = make(chan *netFD, 1);
+ return f, nil
+}
+
+func (fd *netFD) Close() os.Error {
+ if fd == nil || fd.file == nil {
+ return os.EINVAL
+ }
+
+ // In case the user has set linger,
+ // switch to blocking mode so the close blocks.
+ // As long as this doesn't happen often,
+ // we can handle the extra OS processes.
+ // Otherwise we'll need to use the pollserver
+ // for Close too. Sigh.
+ syscall.SetNonblock(fd.file.Fd(), false);
+
+ e := fd.file.Close();
+ fd.file = nil;
+ fd.fd = -1;
+ return e
+}
+
+func (fd *netFD) Read(p []byte) (n int, err os.Error) {
+ if fd == nil || fd.file == nil {
+ return -1, os.EINVAL
+ }
+ fd.rio.Lock();
+ defer fd.rio.Unlock();
+ if fd.rdeadline_delta > 0 {
+ fd.rdeadline = pollserver.Now() + fd.rdeadline_delta;
+ } else {
+ fd.rdeadline = 0;
+ }
+ n, err = fd.file.Read(p);
+ for err == os.EAGAIN && fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd);
+ n, err = fd.file.Read(p)
+ }
+ return n, err
+}
+
+func (fd *netFD) Write(p []byte) (n int, err os.Error) {
+ if fd == nil || fd.file == nil {
+ return -1, os.EINVAL
+ }
+ fd.wio.Lock();
+ defer fd.wio.Unlock();
+ if fd.wdeadline_delta > 0 {
+ fd.wdeadline = pollserver.Now() + fd.wdeadline_delta;
+ } else {
+ fd.wdeadline = 0;
+ }
+ err = nil;
+ nn := 0;
+ for nn < len(p) {
+ n, err = fd.file.Write(p[nn:len(p)]);
+ if n > 0 {
+ nn += n
+ }
+ if nn == len(p) {
+ break;
+ }
+ if err == os.EAGAIN && fd.wdeadline >= 0 {
+ pollserver.WaitWrite(fd);
+ continue;
+ }
+ if n == 0 || err != nil {
+ break;
+ }
+ }
+ return nn, err
+}
+
+func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error)
+
+func (fd *netFD) accept() (nfd *netFD, err os.Error) {
+ if fd == nil || fd.file == nil {
+ return nil, os.EINVAL
+ }
+
+ // See ../syscall/exec.go for description of ForkLock.
+ // It is okay to hold the lock across syscall.Accept
+ // because we have put fd.fd into non-blocking mode.
+ syscall.ForkLock.RLock();
+ var s, e int;
+ var sa syscall.Sockaddr;
+ for {
+ s, sa, e = syscall.Accept(fd.fd);
+ if e != syscall.EAGAIN {
+ break;
+ }
+ syscall.ForkLock.RUnlock();
+ pollserver.WaitRead(fd);
+ syscall.ForkLock.RLock();
+ }
+ if e != 0 {
+ syscall.ForkLock.RUnlock();
+ return nil, os.ErrnoToError(e)
+ }
+ syscall.CloseOnExec(s);
+ syscall.ForkLock.RUnlock();
+
+ raddr, err1 := sockaddrToString(sa);
+ if err1 != nil {
+ raddr = "invalid-address";
+ }
+ if nfd, err = newFD(s, fd.net, fd.laddr, raddr); err != nil {
+ syscall.Close(s);
+ return nil, err
+ }
+ return nfd, nil
+}
+
diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go
new file mode 100644
index 000000000..42bf51221
--- /dev/null
+++ b/src/pkg/net/fd_darwin.go
@@ -0,0 +1,115 @@
+// 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.
+
+// Waiting for FDs via kqueue/kevent.
+
+package net
+
+import (
+ "net";
+ "os";
+ "syscall";
+)
+
+var kqueuePhaseError = &Error{"kqueue phase error"}
+
+type pollster struct {
+ kq int;
+ eventbuf [10]syscall.Kevent_t;
+ events []syscall.Kevent_t;
+}
+
+func newpollster() (p *pollster, err os.Error) {
+ p = new(pollster);
+ var e int;
+ if p.kq, e = syscall.Kqueue(); e != 0 {
+ return nil, os.ErrnoToError(e)
+ }
+ p.events = p.eventbuf[0:0];
+ return p, nil
+}
+
+func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
+ var kmode int;
+ if mode == 'r' {
+ kmode = syscall.EVFILT_READ
+ } else {
+ kmode = syscall.EVFILT_WRITE
+ }
+ var events [1]syscall.Kevent_t;
+ ev := &events[0];
+ // EV_ADD - add event to kqueue list
+ // EV_RECEIPT - generate fake EV_ERROR as result of add,
+ // rather than waiting for real event
+ // EV_ONESHOT - delete the event the first time it triggers
+ flags := syscall.EV_ADD | syscall.EV_RECEIPT;
+ if !repeat {
+ flags |= syscall.EV_ONESHOT
+ }
+ syscall.SetKevent(ev, fd, kmode, flags);
+
+ n, e := syscall.Kevent(p.kq, &events, &events, nil);
+ if e != 0 {
+ return os.ErrnoToError(e)
+ }
+ if n != 1 || (ev.Flags & syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
+ return kqueuePhaseError
+ }
+ if ev.Data != 0 {
+ return os.ErrnoToError(int(ev.Data))
+ }
+ return nil
+}
+
+func (p *pollster) DelFD(fd int, mode int) {
+ var kmode int;
+ if mode == 'r' {
+ kmode = syscall.EVFILT_READ
+ } else {
+ kmode = syscall.EVFILT_WRITE
+ }
+ var events [1]syscall.Kevent_t;
+ ev := &events[0];
+ // EV_DELETE - delete event from kqueue list
+ // EV_RECEIPT - generate fake EV_ERROR as result of add,
+ // rather than waiting for real event
+ syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE | syscall.EV_RECEIPT);
+ syscall.Kevent(p.kq, &events, &events, nil);
+}
+
+func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
+ var t *syscall.Timespec;
+ for len(p.events) == 0 {
+ if nsec > 0 {
+ if t == nil {
+ t = new(syscall.Timespec);
+ }
+ *t = syscall.NsecToTimespec(nsec);
+ }
+ nn, e := syscall.Kevent(p.kq, nil, &p.eventbuf, t);
+ if e != 0 {
+ if e == syscall.EINTR {
+ continue
+ }
+ return -1, 0, os.ErrnoToError(e)
+ }
+ if nn == 0 {
+ return -1, 0, nil;
+ }
+ p.events = p.eventbuf[0:nn]
+ }
+ ev := &p.events[0];
+ p.events = p.events[1:len(p.events)];
+ fd = int(ev.Ident);
+ if ev.Filter == syscall.EVFILT_READ {
+ mode = 'r'
+ } else {
+ mode = 'w'
+ }
+ return fd, mode, nil
+}
+
+func (p *pollster) Close() os.Error {
+ return os.ErrnoToError(syscall.Close(p.kq))
+}
diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go
new file mode 100644
index 000000000..bd822589e
--- /dev/null
+++ b/src/pkg/net/fd_linux.go
@@ -0,0 +1,150 @@
+// 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.
+
+// Waiting for FDs via epoll(7).
+
+package net
+
+import (
+ "net";
+ "os";
+ "syscall";
+)
+
+const (
+ readFlags = syscall.EPOLLIN | syscall.EPOLLRDHUP;
+ writeFlags = syscall.EPOLLOUT
+)
+
+type pollster struct {
+ epfd int;
+
+ // Events we're already waiting for
+ events map[int] uint32;
+}
+
+func newpollster() (p *pollster, err os.Error) {
+ p = new(pollster);
+ var e int;
+
+ // The arg to epoll_create is a hint to the kernel
+ // about the number of FDs we will care about.
+ // We don't know.
+ if p.epfd, e = syscall.EpollCreate(16); e != 0 {
+ return nil, os.ErrnoToError(e)
+ }
+ p.events = make(map[int] uint32);
+ return p, nil
+}
+
+func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
+ var ev syscall.EpollEvent;
+ var already bool;
+ ev.Fd = int32(fd);
+ ev.Events, already = p.events[fd];
+ if !repeat {
+ ev.Events |= syscall.EPOLLONESHOT;
+ }
+ if mode == 'r' {
+ ev.Events |= readFlags;
+ } else {
+ ev.Events |= writeFlags;
+ }
+
+ var op int;
+ if already {
+ op = syscall.EPOLL_CTL_MOD;
+ } else {
+ op = syscall.EPOLL_CTL_ADD;
+ }
+ if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 {
+ return os.ErrnoToError(e)
+ }
+ p.events[fd] = ev.Events;
+ return nil
+}
+
+func (p *pollster) StopWaiting(fd int, bits uint) {
+ events, already := p.events[fd];
+ if !already {
+ print("Epoll unexpected fd=", fd, "\n");
+ return;
+ }
+
+ // If syscall.EPOLLONESHOT is not set, the wait
+ // is a repeating wait, so don't change it.
+ if events & syscall.EPOLLONESHOT == 0 {
+ return;
+ }
+
+ // Disable the given bits.
+ // If we're still waiting for other events, modify the fd
+ // event in the kernel. Otherwise, delete it.
+ events &= ^uint32(bits);
+ if int32(events) & ^syscall.EPOLLONESHOT != 0 {
+ var ev syscall.EpollEvent;
+ ev.Fd = int32(fd);
+ ev.Events = events;
+ if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
+ print("Epoll modify fd=", fd, ": ", os.ErrnoToError(e).String(), "\n");
+ }
+ p.events[fd] = events;
+ } else {
+ if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
+ print("Epoll delete fd=", fd, ": ", os.ErrnoToError(e).String(), "\n");
+ }
+ p.events[fd] = 0, false;
+ }
+}
+
+func (p *pollster) DelFD(fd int, mode int) {
+ if mode == 'r' {
+ p.StopWaiting(fd, readFlags);
+ } else {
+ p.StopWaiting(fd, writeFlags);
+ }
+}
+
+func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
+ // Get an event.
+ var evarray [1]syscall.EpollEvent;
+ ev := &evarray[0];
+ var msec int = -1;
+ if nsec > 0 {
+ msec = int((nsec + 1e6 - 1)/1e6);
+ }
+ n, e := syscall.EpollWait(p.epfd, &evarray, msec);
+ for e == syscall.EAGAIN || e == syscall.EINTR {
+ n, e = syscall.EpollWait(p.epfd, &evarray, msec);
+ }
+ if e != 0 {
+ return -1, 0, os.ErrnoToError(e);
+ }
+ if n == 0 {
+ return -1, 0, nil;
+ }
+ fd = int(ev.Fd);
+
+ if ev.Events & writeFlags != 0 {
+ p.StopWaiting(fd, writeFlags);
+ return fd, 'w', nil;
+ }
+ if ev.Events & readFlags != 0 {
+ p.StopWaiting(fd, readFlags);
+ return fd, 'r', nil;
+ }
+
+ // Other events are error conditions - wake whoever is waiting.
+ events, already := p.events[fd];
+ if events & writeFlags != 0 {
+ p.StopWaiting(fd, writeFlags);
+ return fd, 'w', nil;
+ }
+ p.StopWaiting(fd, readFlags);
+ return fd, 'r', nil;
+}
+
+func (p *pollster) Close() os.Error {
+ return os.ErrnoToError(syscall.Close(p.epfd));
+}
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
new file mode 100644
index 000000000..774f048ca
--- /dev/null
+++ b/src/pkg/net/ip.go
@@ -0,0 +1,421 @@
+// 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.
+
+// IP address manipulations
+//
+// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
+// An IPv4 address can be converted to an IPv6 address by
+// adding a canonical prefix (10 zeros, 2 0xFFs).
+// This library accepts either size of byte array but always
+// returns 16-byte addresses.
+
+package net
+
+import (
+ "net"
+)
+
+// IP address lengths (bytes).
+const (
+ IPv4len = 4;
+ IPv6len = 16
+)
+
+// An IP is a single IP address, an array of bytes.
+// Functions in this package accept either 4-byte (IP v4)
+// or 16-byte (IP v6) arrays as input. Unless otherwise
+// specified, functions in this package always return
+// IP addresses in 16-byte form using the canonical
+// embedding.
+//
+// Note that in this documentation, referring to an
+// IP address as an IPv4 address or an IPv6 address
+// is a semantic property of the address, not just the
+// length of the byte array: a 16-byte array can still
+// be an IPv4 address.
+type IP []byte;
+
+// An IP mask is an IP address.
+type IPMask []byte;
+
+// IPv4 returns the IP address (in 16-byte form) of the
+// IPv4 address a.b.c.d.
+func IPv4(a, b, c, d byte) IP {
+ p := make(IP, IPv6len);
+ for i := 0; i < 10; i++ {
+ p[i] = 0
+ }
+ p[10] = 0xff;
+ p[11] = 0xff;
+ p[12] = a;
+ p[13] = b;
+ p[14] = c;
+ p[15] = d;
+ return p
+}
+
+// Well-known IPv4 addresses
+var (
+ IPv4bcast = IPv4(255, 255, 255, 255); // broadcast
+ IPv4allsys = IPv4(224, 0, 0, 1); // all systems
+ IPv4allrouter = IPv4(224, 0, 0, 2); // all routers
+ IPv4zero = IPv4(0, 0, 0, 0); // all zeros
+)
+
+// Well-known IPv6 addresses
+var (
+ IPzero = make(IP, IPv6len); // all zeros
+)
+
+// Is p all zeros?
+func isZeros(p IP) bool {
+ for i := 0; i < len(p); i++ {
+ if p[i] != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// To4 converts the IPv4 address ip to a 4-byte representation.
+// If ip is not an IPv4 address, To4 returns nil.
+func (ip IP) To4() IP {
+ if len(ip) == IPv4len {
+ return ip
+ }
+ if len(ip) == IPv6len
+ && isZeros(ip[0:10])
+ && ip[10] == 0xff
+ && ip[11] == 0xff {
+ return ip[12:16]
+ }
+ return nil
+}
+
+// To16 converts the IP address ip to a 16-byte representation.
+// If ip is not an IP address (it is the wrong length), To16 returns nil.
+func (ip IP) To16() IP {
+ if len(ip) == IPv4len {
+ return IPv4(ip[0], ip[1], ip[2], ip[3])
+ }
+ if len(ip) == IPv6len {
+ return ip
+ }
+ return nil
+}
+
+// Default route masks for IPv4.
+var (
+ classAMask = IPMask(IPv4(0xff, 0, 0, 0));
+ classBMask = IPMask(IPv4(0xff, 0xff, 0, 0));
+ classCMask = IPMask(IPv4(0xff, 0xff, 0xff, 0));
+)
+
+// DefaultMask returns the default IP mask for the IP address ip.
+// Only IPv4 addresses have default masks; DefaultMask returns
+// nil if ip is not a valid IPv4 address.
+func (ip IP) DefaultMask() IPMask {
+ if ip = ip.To4(); ip == nil {
+ return nil
+ }
+ switch true {
+ case ip[0] < 0x80:
+ return classAMask;
+ case ip[0] < 0xC0:
+ return classBMask;
+ default:
+ return classCMask;
+ }
+ return nil; // not reached
+}
+
+// Mask returns the result of masking the IP address ip with mask.
+func (ip IP) Mask(mask IPMask) IP {
+ n := len(ip);
+ if n != len(mask) {
+ return nil
+ }
+ out := make(IP, n);
+ for i := 0; i < n; i++ {
+ out[i] = ip[i] & mask[i];
+ }
+ return out
+}
+
+// Convert i to decimal string.
+func itod(i uint) string {
+ if i == 0 {
+ return "0"
+ }
+
+ // Assemble decimal in reverse order.
+ var b [32]byte;
+ bp := len(b);
+ for ; i > 0; i /= 10 {
+ bp--;
+ b[bp] = byte(i%10) + '0'
+ }
+
+ return string(b[bp:len(b)])
+}
+
+// Convert i to hexadecimal string.
+func itox(i uint) string {
+ if i == 0 {
+ return "0"
+ }
+
+ // Assemble hexadecimal in reverse order.
+ var b [32]byte;
+ bp := len(b);
+ for ; i > 0; i /= 16 {
+ bp--;
+ b[bp] = "0123456789abcdef"[byte(i%16)]
+ }
+
+ return string(b[bp:len(b)])
+}
+
+// String returns the string form of the IP address ip.
+// If the address is an IPv4 address, the string representation
+// is dotted decimal ("74.125.19.99"). Otherwise the representation
+// is IPv6 ("2001:4860:0:2001::68").
+func (ip IP) String() string {
+ p := ip;
+
+ // If IPv4, use dotted notation.
+ if p4 := p.To4(); len(p4) == 4 {
+ return itod(uint(p4[0]))+"."
+ +itod(uint(p4[1]))+"."
+ +itod(uint(p4[2]))+"."
+ +itod(uint(p4[3]))
+ }
+ if len(p) != IPv6len {
+ return "?"
+ }
+
+ // Find longest run of zeros.
+ e0 := -1;
+ e1 := -1;
+ for i := 0; i < 16; i+=2 {
+ j := i;
+ for j < 16 && p[j] == 0 && p[j+1] == 0 {
+ j += 2
+ }
+ if j > i && j - i > e1 - e0 {
+ e0 = i;
+ e1 = j
+ }
+ }
+
+ // Print with possible :: in place of run of zeros
+ var s string;
+ for i := 0; i < 16; i += 2 {
+ if i == e0 {
+ s += "::";
+ i = e1;
+ if i >= 16 {
+ break
+ }
+ } else if i > 0 {
+ s += ":"
+ }
+ s += itox((uint(p[i])<<8) | uint(p[i+1]))
+ }
+ return s
+}
+
+// If mask is a sequence of 1 bits followed by 0 bits,
+// return the number of 1 bits.
+func simpleMaskLength(mask IPMask) int {
+ var i int;
+ for i = 0; i < len(mask); i++ {
+ if mask[i] != 0xFF {
+ break
+ }
+ }
+ n := 8*i;
+ v := mask[i];
+ for v & 0x80 != 0 {
+ n++;
+ v <<= 1
+ }
+ if v != 0 {
+ return -1
+ }
+ for i++; i < len(mask); i++ {
+ if mask[i] != 0 {
+ return -1
+ }
+ }
+ return n
+}
+
+// String returns the string representation of mask.
+// If the mask is in the canonical form--ones followed by zeros--the
+// string representation is just the decimal number of ones.
+// If the mask is in a non-canonical form, it is formatted
+// as an IP address.
+func (mask IPMask) String() string {
+ switch len(mask) {
+ case 4:
+ n := simpleMaskLength(mask);
+ if n >= 0 {
+ return itod(uint(n+(IPv6len-IPv4len)*8))
+ }
+ case 16:
+ n := simpleMaskLength(mask);
+ if n >= 0 {
+ return itod(uint(n))
+ }
+ }
+ return IP(mask).String();
+}
+
+// Parse IPv4 address (d.d.d.d).
+func parseIPv4(s string) IP {
+ var p [IPv4len]byte;
+ i := 0;
+ for j := 0; j < IPv4len; j++ {
+ if j > 0 {
+ if s[i] != '.' {
+ return nil
+ }
+ i++;
+ }
+ var (
+ n int;
+ ok bool
+ )
+ n, i, ok = dtoi(s, i);
+ if !ok || n > 0xFF {
+ return nil
+ }
+ p[j] = byte(n)
+ }
+ if i != len(s) {
+ return nil
+ }
+ return IPv4(p[0], p[1], p[2], p[3])
+}
+
+// Parse IPv6 address. Many forms.
+// The basic form is a sequence of eight colon-separated
+// 16-bit hex numbers separated by colons,
+// as in 0123:4567:89ab:cdef:0123:4567:89ab:cdef.
+// Two exceptions:
+// * A run of zeros can be replaced with "::".
+// * The last 32 bits can be in IPv4 form.
+// Thus, ::ffff:1.2.3.4 is the IPv4 address 1.2.3.4.
+func parseIPv6(s string) IP {
+ p := make(IP, 16);
+ ellipsis := -1; // position of ellipsis in p
+ i := 0; // index in string s
+
+ // Might have leading ellipsis
+ if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
+ ellipsis = 0;
+ i = 2;
+ // Might be only ellipsis
+ if i == len(s) {
+ return p
+ }
+ }
+
+ // Loop, parsing hex numbers followed by colon.
+ j := 0;
+L: for j < IPv6len {
+ // Hex number.
+ n, i1, ok := xtoi(s, i);
+ if !ok || n > 0xFFFF {
+ return nil
+ }
+
+ // If followed by dot, might be in trailing IPv4.
+ if i1 < len(s) && s[i1] == '.' {
+ if ellipsis < 0 && j != IPv6len - IPv4len {
+ // Not the right place.
+ return nil
+ }
+ if j+IPv4len > IPv6len {
+ // Not enough room.
+ return nil
+ }
+ p4 := parseIPv4(s[i:len(s)]);
+ if p4 == nil {
+ return nil
+ }
+ // BUG: p[j:j+4] = p4
+ p[j] = p4[12];
+ p[j+1] = p4[13];
+ p[j+2] = p4[14];
+ p[j+3] = p4[15];
+ i = len(s);
+ j += 4;
+ break
+ }
+
+ // Save this 16-bit chunk.
+ p[j] = byte(n>>8);
+ p[j+1] = byte(n);
+ j += 2;
+
+ // Stop at end of string.
+ i = i1;
+ if i == len(s) {
+ break
+ }
+
+ // Otherwise must be followed by colon and more.
+ if s[i] != ':' && i+1 == len(s) {
+ return nil
+ }
+ i++;
+
+ // Look for ellipsis.
+ if s[i] == ':' {
+ if ellipsis >= 0 { // already have one
+ return nil
+ }
+ ellipsis = j;
+ if i++; i == len(s) { // can be at end
+ break
+ }
+ }
+ }
+
+ // Must have used entire string.
+ if i != len(s) {
+ return nil
+ }
+
+ // If didn't parse enough, expand ellipsis.
+ if j < IPv6len {
+ if ellipsis < 0 {
+ return nil
+ }
+ n := IPv6len - j;
+ for k := j-1; k >= ellipsis; k-- {
+ p[k+n] = p[k]
+ }
+ for k := ellipsis+n-1; k>=ellipsis; k-- {
+ p[k] = 0
+ }
+ }
+ return p
+}
+
+// ParseIP parses s as an IP address, returning the result.
+// The string s can be in dotted decimal ("74.125.19.99")
+// or IPv6 ("2001:4860:0:2001::68") form.
+// If s is not a valid textual representation of an IP address,
+// ParseIP returns nil.
+func ParseIP(s string) IP {
+ p := parseIPv4(s);
+ if p != nil {
+ return p
+ }
+ return parseIPv6(s)
+}
+
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
new file mode 100644
index 000000000..fb2ae8216
--- /dev/null
+++ b/src/pkg/net/ip_test.go
@@ -0,0 +1,50 @@
+// 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 isEqual(a, b IP) bool {
+ if a == nil && b == nil {
+ 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 IP;
+}
+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",
+ IP{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)},
+}
+
+func TestParseIP(t *testing.T) {
+ for i := 0; i < len(parseiptests); i++ {
+ tt := parseiptests[i];
+ if out := ParseIP(tt.in); !isEqual(out, tt.out) {
+ t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out);
+ }
+ }
+}
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
new file mode 100644
index 000000000..5c442e6a4
--- /dev/null
+++ b/src/pkg/net/net.go
@@ -0,0 +1,862 @@
+// 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";
+ "os";
+ "strconv";
+ "syscall";
+)
+
+var (
+ BadAddress os.Error = &Error{"malformed address"};
+ MissingAddress os.Error = &Error{"missing address"};
+ UnknownNetwork os.Error = &Error{"unknown network"};
+ UnknownHost os.Error = &Error{"unknown host"};
+ UnknownSocketFamily os.Error = &Error{"unknown socket family"};
+)
+
+
+// Conn is a generic network connection.
+type Conn interface {
+ // Read blocks until data is ready from the connection
+ // and then reads into b. It returns the number
+ // of bytes read, or 0 if the connection has been closed.
+ Read(b []byte) (n int, err os.Error);
+
+ // Write writes the data in b to the connection.
+ Write(b []byte) (n int, err os.Error);
+
+ // Close closes the connection.
+ Close() os.Error;
+
+ // For packet-based protocols such as UDP,
+ // ReadFrom reads the next packet from the network,
+ // returning the number of bytes read and the remote
+ // address that sent them.
+ ReadFrom(b []byte) (n int, addr string, err os.Error);
+
+ // For packet-based protocols such as UDP,
+ // WriteTo writes the byte buffer b to the network
+ // as a single payload, sending it to the target address.
+ WriteTo(addr string, b []byte) (n int, err os.Error);
+
+ // SetReadBuffer sets the size of the operating system's
+ // receive buffer associated with the connection.
+ SetReadBuffer(bytes int) os.Error;
+
+ // SetReadBuffer sets the size of the operating system's
+ // transmit buffer associated with the connection.
+ SetWriteBuffer(bytes int) os.Error;
+
+ // SetTimeout sets the read and write deadlines associated
+ // with the connection.
+ SetTimeout(nsec int64) os.Error;
+
+ // SetReadTimeout sets the time (in nanoseconds) that
+ // Read will wait for data before returning os.EAGAIN.
+ // Setting nsec == 0 (the default) disables the deadline.
+ SetReadTimeout(nsec int64) os.Error;
+
+ // SetWriteTimeout sets the time (in nanoseconds) that
+ // Write will wait to send its data before returning os.EAGAIN.
+ // Setting nsec == 0 (the default) disables the deadline.
+ // Even if write times out, it may return n > 0, indicating that
+ // some of the data was successfully written.
+ SetWriteTimeout(nsec int64) os.Error;
+
+ // SetLinger sets the behavior of Close() on a connection
+ // which still has data waiting to be sent or to be acknowledged.
+ //
+ // If sec < 0 (the default), Close returns immediately and
+ // the operating system finishes sending the data in the background.
+ //
+ // If sec == 0, Close returns immediately and the operating system
+ // discards any unsent or unacknowledged data.
+ //
+ // If sec > 0, Close blocks for at most sec seconds waiting for
+ // data to be sent and acknowledged.
+ SetLinger(sec int) os.Error;
+
+ // SetReuseAddr sets whether it is okay to reuse addresses
+ // from recent connections that were not properly closed.
+ SetReuseAddr(reuseaddr bool) os.Error;
+
+ // SetDontRoute sets whether outgoing messages should
+ // bypass the system routing tables.
+ SetDontRoute(dontroute bool) os.Error;
+
+ // SetKeepAlive sets whether the operating system should send
+ // keepalive messages on the connection.
+ SetKeepAlive(keepalive bool) os.Error;
+
+ // BindToDevice binds a connection to a particular network device.
+ BindToDevice(dev string) os.Error;
+}
+
+// Should we try to use the IPv4 socket interface if we're
+// only dealing with IPv4 sockets? As long as the host system
+// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
+// interface. That simplifies our code and is most general.
+// Unfortunately, we need to run on kernels built without IPv6 support too.
+// So probe the kernel to figure it out.
+func kernelSupportsIPv6() bool {
+ fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP);
+ if fd >= 0 {
+ syscall.Close(fd)
+ }
+ return e == 0
+}
+
+var preferIPv4 = !kernelSupportsIPv6()
+
+// TODO(rsc): if syscall.OS == "linux", we're supposd to read
+// /proc/sys/net/core/somaxconn,
+// to take advantage of kernels that have raised the limit.
+func listenBacklog() int {
+ return syscall.SOMAXCONN
+}
+
+func LookupHost(name string) (cname 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) {
+ // The port starts after the last colon.
+ var i int;
+ for i = len(hostport)-1; i >= 0; i-- {
+ if hostport[i] == ':' {
+ break
+ }
+ }
+ if i < 0 {
+ return "", "", BadAddress
+ }
+
+ host = hostport[0:i];
+ port = hostport[i+1:len(hostport)];
+
+ // Can put brackets around host ...
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1:len(host)-1]
+ } else {
+ // ... but if there are no brackets, no colons.
+ if byteIndex(host, ':') >= 0 {
+ return "", "", BadAddress
+ }
+ }
+ return host, port, nil
+}
+
+// Join "host" and "port" into "host:port".
+// If host contains colons, will join into "[host]:port".
+func joinHostPort(host, port string) string {
+ // If host has colons, have to bracket it.
+ if byteIndex(host, ':') >= 0 {
+ return "[" + host + "]:" + port
+ }
+ return host + ":" + port
+}
+
+// Convert "host:port" into IP address and port.
+// For now, host and port must be numeric literals.
+// Eventually, we'll have name resolution.
+func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) {
+ var host, port string;
+ host, port, err = splitHostPort(hostport);
+ if err != nil {
+ return nil, 0, err
+ }
+
+ var addr IP;
+ if host == "" {
+ if mode == "listen" {
+ if preferIPv4 {
+ addr = IPv4zero;
+ } else {
+ addr = IPzero; // wildcard - listen to all
+ }
+ } else {
+ return nil, 0, MissingAddress;
+ }
+ }
+
+ // Try as an IP address.
+ if addr == nil {
+ addr = ParseIP(host);
+ }
+ 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
+ }
+ addr = ParseIP(addrs[0]);
+ if addr == nil {
+ // should not happen
+ return nil, 0, BadAddress
+ }
+ }
+
+ p, i, ok := dtoi(port, 0);
+ if !ok || i != len(port) {
+ p, err = LookupPort(net, port);
+ if err != nil {
+ return nil, 0, err
+ }
+ }
+ if p < 0 || p > 0xFFFF {
+ return nil, 0, BadAddress
+ }
+
+ return addr, p, nil
+}
+
+func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
+ switch a := sa.(type) {
+ case *syscall.SockaddrInet4:
+ return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil;
+ case *syscall.SockaddrInet6:
+ return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil;
+ case *syscall.SockaddrUnix:
+ return a.Name, nil;
+ }
+ return "", UnknownSocketFamily
+}
+
+func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
+ switch family {
+ case syscall.AF_INET:
+ if ip = ip.To4(); ip == nil {
+ return nil, os.EINVAL
+ }
+ s := new(syscall.SockaddrInet4);
+ for i := 0; i < IPv4len; i++ {
+ s.Addr[i] = ip[i];
+ }
+ s.Port = port;
+ return s, nil;
+ case syscall.AF_INET6:
+ // IPv4 callers use 0.0.0.0 to mean "announce on any available address".
+ // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
+ // which it refuses to do. Rewrite to the IPv6 all zeros.
+ if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
+ ip = IPzero;
+ }
+ if ip = ip.To16(); ip == nil {
+ return nil, os.EINVAL
+ }
+ s := new(syscall.SockaddrInet6);
+ for i := 0; i < IPv6len; i++ {
+ s.Addr[i] = ip[i];
+ }
+ s.Port = port;
+ return s, nil;
+ }
+ return nil, os.EINVAL;
+}
+
+// Boolean to int.
+func boolint(b bool) int {
+ if b {
+ return 1
+ }
+ return 0
+}
+
+// Generic socket creation.
+func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) {
+ // See ../syscall/exec.go for description of ForkLock.
+ syscall.ForkLock.RLock();
+ s, e := syscall.Socket(f, p, t);
+ if e != 0 {
+ syscall.ForkLock.RUnlock();
+ return nil, os.ErrnoToError(e)
+ }
+ syscall.CloseOnExec(s);
+ syscall.ForkLock.RUnlock();
+
+ // Allow reuse of recently-used addresses.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1);
+
+ var r int64;
+ if la != nil {
+ e = syscall.Bind(s, la);
+ if e != 0 {
+ syscall.Close(s);
+ return nil, os.ErrnoToError(e)
+ }
+ }
+
+ if ra != nil {
+ e = syscall.Connect(s, ra);
+ if e != 0 {
+ syscall.Close(s);
+ return nil, os.ErrnoToError(e)
+ }
+ }
+
+ fd, err = newFD(s, net, laddr, raddr);
+ if err != nil {
+ syscall.Close(s);
+ return nil, err
+ }
+
+ return fd, nil
+}
+
+
+// Generic implementation of Conn interface; not exported.
+type connBase struct {
+ fd *netFD;
+ raddr string;
+}
+
+func (c *connBase) File() *os.File {
+ if c == nil {
+ return nil
+ }
+ return c.fd.file;
+}
+
+func (c *connBase) sysFD() int {
+ if c == nil || c.fd == nil {
+ return -1;
+ }
+ return c.fd.fd;
+}
+
+func (c *connBase) Read(b []byte) (n int, err os.Error) {
+ n, err = c.fd.Read(b);
+ return n, err
+}
+
+func (c *connBase) Write(b []byte) (n int, err os.Error) {
+ n, err = c.fd.Write(b);
+ return n, err
+}
+
+func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) {
+ if c == nil {
+ return -1, "", os.EINVAL
+ }
+ n, err = c.Read(b);
+ return n, c.raddr, err
+}
+
+func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) {
+ if c == nil {
+ return -1, os.EINVAL
+ }
+ if raddr != c.raddr {
+ return -1, os.EINVAL
+ }
+ n, err = c.Write(b);
+ return n, err
+}
+
+func (c *connBase) Close() os.Error {
+ if c == nil {
+ return os.EINVAL
+ }
+ return c.fd.Close()
+}
+
+
+func setsockoptInt(fd, level, opt int, value int) os.Error {
+ return os.ErrnoToError(syscall.SetsockoptInt(fd, level, opt, value));
+}
+
+func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
+ var tv = syscall.NsecToTimeval(nsec);
+ return os.ErrnoToError(syscall.SetsockoptTimeval(fd, level, opt, &tv));
+}
+
+func (c *connBase) SetReadBuffer(bytes int) os.Error {
+ return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes);
+}
+
+func (c *connBase) SetWriteBuffer(bytes int) os.Error {
+ return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes);
+}
+
+func (c *connBase) SetReadTimeout(nsec int64) os.Error {
+ c.fd.rdeadline_delta = nsec;
+ return nil;
+}
+
+func (c *connBase) SetWriteTimeout(nsec int64) os.Error {
+ c.fd.wdeadline_delta = nsec;
+ return nil;
+}
+
+func (c *connBase) SetTimeout(nsec int64) os.Error {
+ if e := c.SetReadTimeout(nsec); e != nil {
+ return e
+ }
+ return c.SetWriteTimeout(nsec)
+}
+
+func (c *connBase) SetReuseAddr(reuse bool) os.Error {
+ return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse));
+}
+
+func (c *connBase) BindToDevice(dev string) os.Error {
+ // TODO(rsc): call setsockopt with null-terminated string pointer
+ return os.EINVAL
+}
+
+func (c *connBase) SetDontRoute(dontroute bool) os.Error {
+ return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute));
+}
+
+func (c *connBase) SetKeepAlive(keepalive bool) os.Error {
+ return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive));
+}
+
+func (c *connBase) SetLinger(sec int) os.Error {
+ var l syscall.Linger;
+ if sec >= 0 {
+ l.Onoff = 1;
+ l.Linger = int32(sec);
+ } else {
+ l.Onoff = 0;
+ l.Linger = 0;
+ }
+ e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l);
+ return os.ErrnoToError(e);
+}
+
+
+// Internet sockets (TCP, UDP)
+
+func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) {
+ // Parse addresses (unless they are empty).
+ var lip, rip IP;
+ var lport, rport int;
+ var lerr, rerr os.Error;
+
+ if laddr != "" {
+ lip, lport, lerr = hostPortToIP(net, laddr, mode);
+ if lerr != nil {
+ return nil, lerr
+ }
+ }
+ if raddr != "" {
+ rip, rport, rerr = hostPortToIP(net, raddr, mode);
+ if rerr != nil {
+ return nil, rerr
+ }
+ }
+
+ // Figure out IP version.
+ // If network has a suffix like "tcp4", obey it.
+ vers := 0;
+ switch net[len(net)-1] {
+ case '4':
+ vers = 4;
+ case '6':
+ vers = 6;
+ default:
+ // Otherwise, guess.
+ // If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
+ if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) {
+ vers = 4
+ } else {
+ vers = 6
+ }
+ }
+
+ var family int;
+ if vers == 4 {
+ family = syscall.AF_INET
+ } else {
+ family = syscall.AF_INET6
+ }
+
+ var la, ra syscall.Sockaddr;
+ if lip != nil {
+ la, lerr = ipToSockaddr(family, lip, lport);
+ if lerr != nil {
+ return nil, lerr
+ }
+ }
+ if rip != nil {
+ ra, rerr = ipToSockaddr(family, rip, rport);
+ if rerr != nil {
+ return nil, rerr
+ }
+ }
+
+ fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra);
+ return fd, err
+}
+
+
+// TCP connections.
+
+// ConnTCP is an implementation of the Conn interface
+// for TCP network connections.
+type ConnTCP struct {
+ connBase
+}
+
+func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error {
+ if c == nil {
+ return os.EINVAL
+ }
+ return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay))
+}
+
+func newConnTCP(fd *netFD, raddr string) *ConnTCP {
+ c := new(ConnTCP);
+ c.fd = fd;
+ c.raddr = raddr;
+ c.SetNoDelay(true);
+ return c
+}
+
+// DialTCP is like Dial but can only connect to TCP networks
+// and returns a ConnTCP structure.
+func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) {
+ if raddr == "" {
+ return nil, MissingAddress
+ }
+ fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial");
+ if e != nil {
+ return nil, e
+ }
+ return newConnTCP(fd, raddr), nil
+}
+
+
+// UDP connections.
+
+// TODO(rsc): UDP headers mode
+
+// ConnUDP is an implementation of the Conn interface
+// for UDP network connections.
+type ConnUDP struct {
+ connBase
+}
+
+func newConnUDP(fd *netFD, raddr string) *ConnUDP {
+ c := new(ConnUDP);
+ c.fd = fd;
+ c.raddr = raddr;
+ return c
+}
+
+// DialUDP is like Dial but can only connect to UDP networks
+// and returns a ConnUDP structure.
+func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) {
+ if raddr == "" {
+ return nil, MissingAddress
+ }
+ fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial");
+ if e != nil {
+ return nil, e
+ }
+ return newConnUDP(fd, raddr), nil
+}
+
+
+// TODO: raw IP connections
+
+// TODO: raw ethernet connections
+
+
+// Unix domain sockets
+
+func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) {
+ var proto int;
+ switch net {
+ default:
+ return nil, UnknownNetwork;
+ case "unix":
+ proto = syscall.SOCK_STREAM;
+ case "unix-dgram":
+ proto = syscall.SOCK_DGRAM;
+ }
+
+ var la, ra syscall.Sockaddr;
+ switch mode {
+ case "dial":
+ if laddr != "" {
+ return nil, BadAddress;
+ }
+ if raddr == "" {
+ return nil, MissingAddress;
+ }
+ ra = &syscall.SockaddrUnix{Name: raddr};
+
+ case "listen":
+ if laddr == "" {
+ return nil, MissingAddress;
+ }
+ la = &syscall.SockaddrUnix{Name: laddr};
+ if raddr != "" {
+ return nil, BadAddress;
+ }
+ }
+
+ fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra);
+ return fd, err
+}
+
+// ConnUnix is an implementation of the Conn interface
+// for connections to Unix domain sockets.
+type ConnUnix struct {
+ connBase
+}
+
+func newConnUnix(fd *netFD, raddr string) *ConnUnix {
+ c := new(ConnUnix);
+ c.fd = fd;
+ c.raddr = raddr;
+ return c;
+}
+
+// DialUnix is like Dial but can only connect to Unix domain sockets
+// and returns a ConnUnix structure. The laddr argument must be
+// the empty string; it is included only to match the signature of
+// the other dial routines.
+func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) {
+ fd, e := unixSocket(net, laddr, raddr, "dial");
+ if e != nil {
+ return nil, e
+ }
+ return newConnUnix(fd, raddr), nil;
+}
+
+// ListenerUnix is a Unix domain socket listener.
+// Clients should typically use variables of type Listener
+// instead of assuming Unix domain sockets.
+type ListenerUnix struct {
+ fd *netFD;
+ laddr string
+}
+
+// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
+// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets).
+func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) {
+ fd, e := unixSocket(net, laddr, "", "listen");
+ if e != nil {
+ // Check for socket ``in use'' but ``refusing connections,''
+ // which means some program created it and exited
+ // without unlinking it from the file system.
+ // Clean up on that program's behalf and try again.
+ // Don't do this for Linux's ``abstract'' sockets, which begin with @.
+ if e != os.EADDRINUSE || laddr[0] == '@' {
+ return nil, e;
+ }
+ fd1, e1 := unixSocket(net, "", laddr, "dial");
+ if e1 == nil {
+ fd1.Close();
+ }
+ if e1 != os.ECONNREFUSED {
+ return nil, e;
+ }
+ syscall.Unlink(laddr);
+ fd1, e1 = unixSocket(net, laddr, "", "listen");
+ if e1 != nil {
+ return nil, e;
+ }
+ fd = fd1;
+ }
+ e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
+ if e1 != 0 {
+ syscall.Close(fd.fd);
+ return nil, os.ErrnoToError(e1);
+ }
+ return &ListenerUnix{fd, laddr}, nil;
+}
+
+// AcceptUnix accepts the next incoming call and returns the new connection
+// and the remote address.
+func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) {
+ if l == nil || l.fd == nil || l.fd.fd < 0 {
+ return nil, "", os.EINVAL
+ }
+ fd, e := l.fd.accept();
+ if e != nil {
+ return nil, "", e
+ }
+ return newConnUnix(fd, fd.raddr), raddr, nil
+}
+
+// Accept implements the Accept method in the Listener interface;
+// it waits for the next call and returns a generic Conn.
+func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) {
+ // TODO(rsc): 6g bug prevents saying
+ // c, raddr, err = l.AcceptUnix();
+ // return;
+ c1, r1, e1 := l.AcceptUnix();
+ return c1, r1, e1;
+}
+
+
+// Close stops listening on the Unix address.
+// Already accepted connections are not closed.
+func (l *ListenerUnix) Close() os.Error {
+ if l == nil || l.fd == nil {
+ return os.EINVAL
+ }
+
+ // The operating system doesn't clean up
+ // the file that announcing created, so
+ // we have to clean it up ourselves.
+ // There's a race here--we can't know for
+ // sure whether someone else has come along
+ // and replaced our socket name already--
+ // but this sequence (remove then close)
+ // is at least compatible with the auto-remove
+ // sequence in ListenUnix. It's only non-Go
+ // programs that can mess us up.
+ if l.laddr[0] != '@' {
+ syscall.Unlink(l.laddr);
+ }
+ err := l.fd.Close();
+ l.fd = nil;
+ return err;
+}
+
+// Dial connects to the remote address raddr on the network net.
+// If the string laddr is not empty, it is used as the local address
+// for the connection.
+//
+// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
+// "udp", "udp4" (IPv4-only), and "udp6" (IPv6-only).
+//
+// For IP networks, addresses have the form host:port. If host is
+// a literal IPv6 address, it must be enclosed in square brackets.
+//
+// Examples:
+// Dial("tcp", "", "12.34.56.78:80")
+// Dial("tcp", "", "google.com:80")
+// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
+// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
+func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ c, err := DialTCP(net, laddr, raddr);
+ if err != nil {
+ return nil, err
+ }
+ return c, nil;
+ case "udp", "udp4", "upd6":
+ c, err := DialUDP(net, laddr, raddr);
+ return c, err;
+ case "unix", "unix-dgram":
+ c, err := DialUnix(net, laddr, raddr);
+ return c, err;
+/*
+ case "ether":
+ c, err := DialEther(net, laddr, raddr);
+ return c, err;
+ case "ipv4":
+ c, err := DialIPv4(net, laddr, raddr);
+ return c, err;
+ case "ipv6":
+ c, err := DialIPv6(net, laddr, raddr);
+ return c, err
+*/
+ }
+ return nil, UnknownNetwork
+}
+
+// A Listener is a generic network listener.
+// Accept waits for the next connection and Close closes the connection.
+type Listener interface {
+ Accept() (c Conn, raddr string, err os.Error);
+ Close() os.Error;
+}
+
+// ListenerTCP is a TCP network listener.
+// Clients should typically use variables of type Listener
+// instead of assuming TCP.
+type ListenerTCP struct {
+ fd *netFD;
+ laddr string
+}
+
+// ListenTCP announces on the TCP address laddr and returns a TCP listener.
+// Net must be "tcp", "tcp4", or "tcp6".
+func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) {
+ fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen");
+ if e != nil {
+ return nil, e
+ }
+ e1 := syscall.Listen(fd.fd, listenBacklog());
+ if e1 != 0 {
+ syscall.Close(fd.fd);
+ return nil, os.ErrnoToError(e1)
+ }
+ l = new(ListenerTCP);
+ l.fd = fd;
+ return l, nil
+}
+
+// AcceptTCP accepts the next incoming call and returns the new connection
+// and the remote address.
+func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) {
+ if l == nil || l.fd == nil || l.fd.fd < 0 {
+ return nil, "", os.EINVAL
+ }
+ fd, e := l.fd.accept();
+ if e != nil {
+ return nil, "", e
+ }
+ return newConnTCP(fd, fd.raddr), fd.raddr, nil
+}
+
+// Accept implements the Accept method in the Listener interface;
+// it waits for the next call and returns a generic Conn.
+func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) {
+ c1, r1, e1 := l.AcceptTCP();
+ if e1 != nil {
+ return nil, "", e1
+ }
+ return c1, r1, nil
+}
+
+// Close stops listening on the TCP address.
+// Already Accepted connections are not closed.
+func (l *ListenerTCP) Close() os.Error {
+ if l == nil || l.fd == nil {
+ return os.EINVAL
+ }
+ return l.fd.Close()
+}
+
+// Listen announces on the local network address laddr.
+// The network string net must be "tcp", "tcp4", "tcp6",
+// "unix", or "unix-dgram".
+func Listen(net, laddr string) (l Listener, err os.Error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ l, err := ListenTCP(net, laddr);
+ if err != nil {
+ return nil, err;
+ }
+ return l, nil;
+ case "unix", "unix-dgram":
+ l, err := ListenUnix(net, laddr);
+ if err != nil {
+ return nil, err;
+ }
+ return l, nil;
+/*
+ more here
+*/
+ // BUG(rsc): Listen should support UDP.
+ }
+ return nil, UnknownNetwork
+}
+
diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go
new file mode 100644
index 000000000..de47cb812
--- /dev/null
+++ b/src/pkg/net/parse.go
@@ -0,0 +1,160 @@
+// 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";
+)
+
+type Error struct {
+ os.ErrorString
+}
+
+type file struct {
+ file *os.File;
+ data []byte;
+}
+
+func (f *file) close() {
+ f.file.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.FullRead(f.file, f.data[ln:cap(f.data)]);
+ if n >= 0 {
+ f.data = f.data[0:ln+n];
+ }
+ }
+ s, ok = f.getLineFromData();
+ return
+}
+
+func open(name string) (*file, os.Error) {
+ fd, err := os.Open(name, os.O_RDONLY, 0);
+ if err != nil {
+ return nil, err;
+ }
+ return &file{fd, make([]byte, 1024)[0:0]}, nil;
+}
+
+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.
+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.
+func splitAtBytes(s string, t string) []string {
+ a := make([]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];
+}
+
+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.
+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.
+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
+}
+
diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go
new file mode 100644
index 000000000..ce0bb4709
--- /dev/null
+++ b/src/pkg/net/parse_test.go
@@ -0,0 +1,44 @@
+// 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";
+)
+
+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 := bufio.NewReader(fd);
+
+ var file *file;
+ file, err = 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;
+ }
+}
diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go
new file mode 100644
index 000000000..21e3b48aa
--- /dev/null
+++ b/src/pkg/net/port.go
@@ -0,0 +1,77 @@
+// 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";
+)
+
+// The error returned by LookupPort when a network service
+// is not listed in the database.
+var ErrNoService = &Error{"unknown network service"};
+
+var services map[string] map[string] int
+var servicesError os.Error
+
+func readServices() {
+ services = make(map[string] map[string] int);
+ var file *file;
+ file, servicesError = 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 = make(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();
+}
+
+// LookupPort looks up the port for the given network and service.
+func LookupPort(network, service string) (port int, err os.Error) {
+ once.Do(readServices);
+
+ switch network {
+ case "tcp4", "tcp6":
+ network = "tcp";
+ case "udp4", "udp6":
+ network = "udp";
+ }
+
+ m, ok := services[network];
+ if !ok {
+ return 0, ErrNoService;
+ }
+ port, ok = m[service];
+ if !ok {
+ return 0, ErrNoService;
+ }
+ return port, nil;
+}
diff --git a/src/pkg/net/port_test.go b/src/pkg/net/port_test.go
new file mode 100644
index 000000000..c535d4caa
--- /dev/null
+++ b/src/pkg/net/port_test.go
@@ -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 },
+}
+
+func TestLookupPort(t *testing.T) {
+ for i := 0; i < len(porttests); i++ {
+ tt := porttests[i];
+ if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok {
+ t.Errorf("LookupPort(%q, %q) = %v, %s; want %v",
+ tt.netw, tt.name, port, err, tt.port);
+ }
+ }
+}
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
new file mode 100644
index 000000000..586b55365
--- /dev/null
+++ b/src/pkg/net/server_test.go
@@ -0,0 +1,93 @@
+// 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 (
+ "io";
+ "net";
+ "os";
+ "syscall";
+ "testing";
+)
+
+func runEcho(fd io.ReadWriter, done chan<- int) {
+ var buf [1024]byte;
+
+ for {
+ n, err := fd.Read(&buf);
+ if err != nil || n == 0 {
+ break;
+ }
+ fd.Write(buf[0:n])
+ }
+ done <- 1
+}
+
+func runServe(t *testing.T, network, addr string, listening, done chan<- int) {
+ l, err := net.Listen(network, addr);
+ if err != nil {
+ t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err);
+ }
+ listening <- 1;
+
+ for {
+ fd, addr, err := l.Accept();
+ if err != nil {
+ break;
+ }
+ echodone := make(chan int);
+ go runEcho(fd, echodone);
+ <-echodone; // make sure Echo stops
+ l.Close();
+ }
+ done <- 1
+}
+
+func connect(t *testing.T, network, addr string) {
+ fd, err := net.Dial(network, "", addr);
+ if err != nil {
+ t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err);
+ }
+
+ b := io.StringBytes("hello, world\n");
+ var b1 [100]byte;
+
+ n, errno := fd.Write(b);
+ if n != len(b) {
+ t.Fatalf("fd.Write(%q) = %d, %v", b, n, errno);
+ }
+
+ n, errno = fd.Read(&b1);
+ if n != len(b) {
+ t.Fatalf("fd.Read() = %d, %v", n, errno);
+ }
+ fd.Close();
+}
+
+func doTest(t *testing.T, network, listenaddr, dialaddr string) {
+ t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr);
+ listening := make(chan int);
+ done := make(chan int);
+ go runServe(t, network, listenaddr, listening, done);
+ <-listening; // wait for server to start
+ connect(t, network, dialaddr);
+ <-done; // make sure server stopped
+}
+
+func TestTcpServer(t *testing.T) {
+ doTest(t, "tcp", "0.0.0.0:9997", "127.0.0.1:9997");
+ doTest(t, "tcp", "[::]:9997", "[::ffff:127.0.0.1]:9997");
+ doTest(t, "tcp", "[::]:9997", "127.0.0.1:9997");
+ doTest(t, "tcp", ":9997", "127.0.0.1:9997");
+ doTest(t, "tcp", "0.0.0.0:9997", "[::ffff:127.0.0.1]:9997");
+}
+
+func TestUnixServer(t *testing.T) {
+ doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net");
+ if syscall.OS == "linux" {
+ // Test abstract unix domain socket, a Linux-ism
+ doTest(t, "unix", "@gotest/net", "@gotest/net");
+ }
+}
diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go
new file mode 100644
index 000000000..e08ce88ce
--- /dev/null
+++ b/src/pkg/net/timeout_test.go
@@ -0,0 +1,42 @@
+// 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";
+ "os";
+ "testing";
+ "time";
+)
+
+func testTimeout(t *testing.T, network, addr string) {
+ fd, err := net.Dial(network, "", addr);
+ defer fd.Close();
+ if err != nil {
+ t.Errorf("dial %s %s failed: %v", network, addr, err);
+ }
+ t0 := time.Nanoseconds();
+ fd.SetReadTimeout(1e8); // 100ms
+ var b [100]byte;
+ n, err1 := fd.Read(&b);
+ t1 := time.Nanoseconds();
+ if n != 0 || err1 != os.EAGAIN {
+ t.Errorf("fd.Read on %s %s did not return 0, EAGAIN: %v, %v", network, addr, n, err1);
+ }
+ if t1 - t0 < 0.5e8 || t1 - t0 > 1.5e8 {
+ t.Errorf("fd.Read on %s %s took %f seconds, expected 0.1", network, addr, float64(t1 - t0) / 1e9);
+ }
+}
+
+func TestTimeoutUDP(t *testing.T) {
+ testTimeout(t, "udp", "127.0.0.1:53");
+}
+
+func TestTimeoutTCP(t *testing.T) {
+ // 74.125.19.99 is www.google.com.
+ // could use dns, but dns depends on
+ // timeouts and this is the timeout test.
+ testTimeout(t, "tcp", "74.125.19.99:80");
+}