From 01c7b7934d5a6b33a2195c90d17cbad6da893e99 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 10 Oct 2014 23:56:35 -0700 Subject: [PATCH 1/5] moved net stuff into subpkg --- net.go => net/convert.go | 28 +++++++++++++++------------- net_test.go => net/convert_test.go | 22 ++++++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) rename net.go => net/convert.go (79%) rename net_test.go => net/convert_test.go (82%) diff --git a/net.go b/net/convert.go similarity index 79% rename from net.go rename to net/convert.go index cddebd0..053dc4a 100644 --- a/net.go +++ b/net/convert.go @@ -1,15 +1,17 @@ -package multiaddr +package net import ( "fmt" "net" "strings" + + ma "github.com/jbenet/go-multiaddr" ) var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion") // FromNetAddr converts a net.Addr type to a Multiaddr. -func FromNetAddr(a net.Addr) (Multiaddr, error) { +func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { switch a.Network() { case "tcp", "tcp4", "tcp6": ac, ok := a.(*net.TCPAddr) @@ -24,7 +26,7 @@ func FromNetAddr(a net.Addr) (Multiaddr, error) { } // Get TCP Addr - tcpm, err := NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port)) + tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port)) if err != nil { return nil, errIncorrectNetAddr } @@ -45,7 +47,7 @@ func FromNetAddr(a net.Addr) (Multiaddr, error) { } // Get UDP Addr - udpm, err := NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port)) + udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port)) if err != nil { return nil, errIncorrectNetAddr } @@ -68,8 +70,8 @@ func FromNetAddr(a net.Addr) (Multiaddr, error) { // ToNetAddr converts a Multiaddr to a net.Addr // Must be ThinWaist. acceptable protocol stacks are: // /ip{4,6}/{tcp, udp} -func ToNetAddr(ma Multiaddr) (net.Addr, error) { - network, host, err := DialArgs(ma) +func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) { + network, host, err := DialArgs(maddr) if err != nil { return nil, err } @@ -87,19 +89,19 @@ func ToNetAddr(ma Multiaddr) (net.Addr, error) { } // FromIP converts a net.IP type to a Multiaddr. -func FromIP(ip net.IP) (Multiaddr, error) { +func FromIP(ip net.IP) (ma.Multiaddr, error) { switch { case ip.To4() != nil: - return NewMultiaddr("/ip4/" + ip.String()) + return ma.NewMultiaddr("/ip4/" + ip.String()) case ip.To16() != nil: - return NewMultiaddr("/ip6/" + ip.String()) + return ma.NewMultiaddr("/ip6/" + ip.String()) default: return nil, errIncorrectNetAddr } } // DialArgs is a convenience function returning arguments for use in net.Dial -func DialArgs(m Multiaddr) (string, string, error) { +func DialArgs(m ma.Multiaddr) (string, string, error) { if !IsThinWaist(m) { return "", "", fmt.Errorf("%s is not a 'thin waist' address", m) } @@ -124,7 +126,7 @@ func DialArgs(m Multiaddr) (string, string, error) { // IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols. // This means: /{IP4, IP6}[/{TCP, UDP}] -func IsThinWaist(m Multiaddr) bool { +func IsThinWaist(m ma.Multiaddr) bool { p := m.Protocols() // nothing? not even a waist. @@ -132,7 +134,7 @@ func IsThinWaist(m Multiaddr) bool { return false } - if p[0].Code != P_IP4 && p[0].Code != P_IP6 { + if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 { return false } @@ -142,7 +144,7 @@ func IsThinWaist(m Multiaddr) bool { } switch p[1].Code { - case P_TCP, P_UDP, P_IP4, P_IP6: + case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6: return true default: return false diff --git a/net_test.go b/net/convert_test.go similarity index 82% rename from net_test.go rename to net/convert_test.go index 5edb560..cdcdc6c 100644 --- a/net_test.go +++ b/net/convert_test.go @@ -1,11 +1,13 @@ -package multiaddr +package net import ( "net" "testing" + + ma "github.com/jbenet/go-multiaddr" ) -type GenFunc func() (Multiaddr, error) +type GenFunc func() (ma.Multiaddr, error) func testConvert(t *testing.T, s string, gen GenFunc) { m, err := gen() @@ -19,7 +21,7 @@ func testConvert(t *testing.T, s string, gen GenFunc) { } func testToNetAddr(t *testing.T, maddr, ntwk, addr string) { - m, err := NewMultiaddr(maddr) + m, err := ma.NewMultiaddr(maddr) if err != nil { t.Fatal("failed to generate.") } @@ -57,19 +59,19 @@ func testToNetAddr(t *testing.T, maddr, ntwk, addr string) { } func TestFromIP4(t *testing.T) { - testConvert(t, "/ip4/10.20.30.40", func() (Multiaddr, error) { + testConvert(t, "/ip4/10.20.30.40", func() (ma.Multiaddr, error) { return FromIP(net.ParseIP("10.20.30.40")) }) } func TestFromIP6(t *testing.T) { - testConvert(t, "/ip6/2001:4860:0:2001::68", func() (Multiaddr, error) { + testConvert(t, "/ip6/2001:4860:0:2001::68", func() (ma.Multiaddr, error) { return FromIP(net.ParseIP("2001:4860:0:2001::68")) }) } func TestFromTCP(t *testing.T) { - testConvert(t, "/ip4/10.20.30.40/tcp/1234", func() (Multiaddr, error) { + testConvert(t, "/ip4/10.20.30.40/tcp/1234", func() (ma.Multiaddr, error) { return FromNetAddr(&net.TCPAddr{ IP: net.ParseIP("10.20.30.40"), Port: 1234, @@ -78,7 +80,7 @@ func TestFromTCP(t *testing.T) { } func TestFromUDP(t *testing.T) { - testConvert(t, "/ip4/10.20.30.40/udp/1234", func() (Multiaddr, error) { + testConvert(t, "/ip4/10.20.30.40/udp/1234", func() (ma.Multiaddr, error) { return FromNetAddr(&net.UDPAddr{ IP: net.ParseIP("10.20.30.40"), Port: 1234, @@ -103,19 +105,19 @@ func TestThinWaist(t *testing.T) { } for a, res := range addrs { - ma, err := NewMultiaddr(a) + m, err := ma.NewMultiaddr(a) if err != nil { t.Fatalf("failed to construct Multiaddr: %s", a) } - if IsThinWaist(ma) != res { + if IsThinWaist(m) != res { t.Fatalf("IsThinWaist(%s) != %v", a, res) } } } func TestDialArgs(t *testing.T) { - m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234") + m, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234") if err != nil { t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234") } From 7d1a12d59be78af82e0b3a519af2b066453cd304 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 11 Oct 2014 01:23:37 -0700 Subject: [PATCH 2/5] documentation --- net/README.md | 11 +++++++++++ net/doc.go | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 net/README.md create mode 100644 net/doc.go diff --git a/net/README.md b/net/README.md new file mode 100644 index 0000000..41f1cb3 --- /dev/null +++ b/net/README.md @@ -0,0 +1,11 @@ +# multiaddr/net - Multiaddr friendly net + +Package multiaddr/net provides Multiaddr specific versions of common +functions in stdlib's net package. This means wrappers of +standard net symbols like net.Dial and net.Listen, as well +as conversion to/from net.Addr. + +Docs: + +- `multiaddr/net`: https://godoc.org/github.com/jbenet/go-multiaddr/net +- `multiaddr`: https://godoc.org/github.com/jbenet/go-multiaddr diff --git a/net/doc.go b/net/doc.go new file mode 100644 index 0000000..d73b865 --- /dev/null +++ b/net/doc.go @@ -0,0 +1,5 @@ +// Package net provides Multiaddr specific versions of common +// functions in stdlib's net package. This means wrappers of +// standard net symbols like net.Dial and net.Listen, as well +// as conversion to/from net.Addr. +package net From 83543fff5dad5798a46f058c0a201f4e2124221a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 11 Oct 2014 01:23:49 -0700 Subject: [PATCH 3/5] basic net package (no tests yet) Implements: - Conn - Dial, Dialer - Listen, Listener --- net/net.go | 218 ++++++++++++++++++++++++++++++++++++++++++++++++ net/net_test.go | 1 + 2 files changed, 219 insertions(+) create mode 100644 net/net.go create mode 100644 net/net_test.go diff --git a/net/net.go b/net/net.go new file mode 100644 index 0000000..07bb3c1 --- /dev/null +++ b/net/net.go @@ -0,0 +1,218 @@ +package net + +import ( + "fmt" + "net" + + ma "github.com/jbenet/go-multiaddr" +) + +// Conn is the equivalent of a net.Conn object. It is the +// result of calling the Dial or Listen functions in this +// package, with associated local and remote Multiaddrs. +type Conn interface { + net.Conn + + // LocalMultiaddr returns the local Multiaddr associated + // with this connection + LocalMultiaddr() ma.Multiaddr + + // RemoteMultiaddr returns the remote Multiaddr associated + // with this connection + RemoteMultiaddr() ma.Multiaddr +} + +// WrapNetConn wraps a net.Conn object with a Multiaddr +// friendly Conn. +func WrapNetConn(nconn net.Conn) (Conn, error) { + + laddr, err := FromNetAddr(nconn.LocalAddr()) + if err != nil { + return nil, fmt.Errorf("failed to convert nconn.LocalAddr: %s", err) + } + + raddr, err := FromNetAddr(nconn.RemoteAddr()) + if err != nil { + return nil, fmt.Errorf("failed to convert nconn.RemoteAddr: %s", err) + } + + return &maConn{ + Conn: nconn, + laddr: laddr, + raddr: raddr, + }, nil +} + +// maConn implements the Conn interface. It's a thin wrapper +// around a net.Conn +type maConn struct { + net.Conn + laddr ma.Multiaddr + raddr ma.Multiaddr +} + +// LocalMultiaddr returns the local address associated with +// this connection +func (c *maConn) LocalMultiaddr() ma.Multiaddr { + return c.laddr +} + +// RemoteMultiaddr returns the remote address associated with +// this connection +func (c *maConn) RemoteMultiaddr() ma.Multiaddr { + return c.raddr +} + +// Dialer contains options for connecting to an address. It +// is effectively the same as net.Dialer, but its LocalAddr +// and RemoteAddr options are Multiaddrs, instead of net.Addrs. +type Dialer struct { + + // Dialer is just an embed net.Dialer, with all its options. + net.Dialer + + // LocalAddr is the local address to use when dialing an + // address. The address must be of a compatible type for the + // network being dialed. + // If nil, a local address is automatically chosen. + LocalAddr ma.Multiaddr +} + +// Dial connects to a remote address, using the options of the +// Dialer. Dialer uses an underlying net.Dialer to Dial a +// net.Conn, then wraps that in a Conn object (with local and +// remote Multiaddrs). +func (d *Dialer) Dial(remote ma.Multiaddr) (Conn, error) { + + // if a LocalAddr is specified, use it on the embedded dialer. + if d.LocalAddr != nil { + // convert our multiaddr to net.Addr friendly + naddr, err := ToNetAddr(d.LocalAddr) + if err != nil { + return nil, err + } + + // set the dialer's LocalAddr as naddr + d.Dialer.LocalAddr = naddr + } + + // get the net.Dial friendly arguments from the remote addr + rnet, rnaddr, err := DialArgs(remote) + if err != nil { + return nil, err + } + + // ok, Dial! + nconn, err := d.Dialer.Dial(rnet, rnaddr) + if err != nil { + return nil, err + } + + // get local address (pre-specified or assigned within net.Conn) + local := d.LocalAddr + if local == nil { + local, err = FromNetAddr(nconn.LocalAddr()) + if err != nil { + return nil, err + } + } + + return &maConn{ + Conn: nconn, + laddr: local, + raddr: remote, + }, nil +} + +// Dial connects to a remote address. It uses an underlying net.Conn, +// then wraps it in a Conn object (with local and remote Multiaddrs). +func Dial(remote ma.Multiaddr) (Conn, error) { + return (&Dialer{}).Dial(remote) +} + +// A Listener is a generic network listener for stream-oriented protocols. +// it uses an embedded net.Listener, overriding net.Listener.Accept to +// return a Conn and providing Multiaddr. +type Listener interface { + + // NetListener returns the embedded net.Listener. Use with caution. + NetListener() net.Listener + + // Accept waits for and returns the next connection to the listener. + // Returns a Multiaddr friendly Conn + Accept() (Conn, error) + + // Close closes the listener. + // Any blocked Accept operations will be unblocked and return errors. + Close() error + + // Multiaddr returns the listener's (local) Multiaddr. + Multiaddr() ma.Multiaddr + + // Addr returns the net.Listener's network address. + Addr() net.Addr +} + +// maListener implements Listener +type maListener struct { + net.Listener + laddr ma.Multiaddr +} + +// NetListener returns the embedded net.Listener. Use with caution. +func (l *maListener) NetListener() net.Listener { + return l.Listener +} + +// Accept waits for and returns the next connection to the listener. +// Returns a Multiaddr friendly Conn +func (l *maListener) Accept() (Conn, error) { + nconn, err := l.Listener.Accept() + if err != nil { + return nil, err + } + + raddr, err := FromNetAddr(nconn.RemoteAddr()) + if err != nil { + return nil, fmt.Errorf("failed to convert connn.RemoteAddr: %s", err) + } + + return &maConn{ + Conn: nconn, + laddr: l.laddr, + raddr: raddr, + }, nil +} + +// Multiaddr returns the listener's (local) Multiaddr. +func (l *maListener) Multiaddr() ma.Multiaddr { + return l.laddr +} + +// Addr returns the listener's network address. +func (l *maListener) Addr() net.Addr { + return l.Listener.Addr() +} + +// Listen announces on the local network address laddr. +// The Multiaddr must be a "ThinWaist" stream-oriented network: +// ip4/tcp, ip6/tcp, (TODO: unix, unixpacket) +// See Dial for the syntax of laddr. +func Listen(laddr ma.Multiaddr) (Listener, error) { + + // get the net.Listen friendly arguments from the remote addr + lnet, lnaddr, err := DialArgs(laddr) + if err != nil { + return nil, err + } + + nl, err := net.Listen(lnet, lnaddr) + if err != nil { + return nil, err + } + + return &maListener{ + Listener: nl, + laddr: laddr, + }, nil +} diff --git a/net/net_test.go b/net/net_test.go new file mode 100644 index 0000000..9d9f1a1 --- /dev/null +++ b/net/net_test.go @@ -0,0 +1 @@ +package net From d169b2f21de4ccf1a6f4ca35f49789d8fbbab858 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 11 Oct 2014 02:28:32 -0700 Subject: [PATCH 4/5] tests: Dial + Listen --- net/net_test.go | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/net/net_test.go b/net/net_test.go index 9d9f1a1..bfcdd18 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -1 +1,200 @@ package net + +import ( + "bytes" + "net" + "sync" + "testing" + + ma "github.com/jbenet/go-multiaddr" +) + +func newMultiaddr(t *testing.T, m string) ma.Multiaddr { + maddr, err := ma.NewMultiaddr(m) + if err != nil { + t.Fatalf("failed to construct multiaddr: %s", m) + } + return maddr +} + +func TestDial(t *testing.T) { + + listener, err := net.Listen("tcp", "127.0.0.1:4321") + if err != nil { + t.Fatal("failed to listen") + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + + cB, err := listener.Accept() + if err != nil { + t.Fatal("failed to accept") + } + + // echo out + buf := make([]byte, 1024) + for { + _, err := cB.Read(buf) + if err != nil { + break + } + cB.Write(buf) + } + + wg.Done() + }() + + maddr := newMultiaddr(t, "/ip4/127.0.0.1/tcp/4321") + cA, err := Dial(maddr) + if err != nil { + t.Fatal("failed to dial") + } + + buf := make([]byte, 1024) + if _, err := cA.Write([]byte("beep boop")); err != nil { + t.Fatal("failed to write:", err) + } + + if _, err := cA.Read(buf); err != nil { + t.Fatal("failed to read:", buf, err) + } + + if !bytes.Equal(buf[:9], []byte("beep boop")) { + t.Fatal("failed to echo:", buf) + } + + maddr2 := cA.RemoteMultiaddr() + if !maddr2.Equal(maddr) { + t.Fatal("remote multiaddr not equal:", maddr, maddr2) + } + + cA.Close() + wg.Wait() +} + +func TestListen(t *testing.T) { + + maddr := newMultiaddr(t, "/ip4/127.0.0.1/tcp/4322") + listener, err := Listen(maddr) + if err != nil { + t.Fatal("failed to listen") + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + + cB, err := listener.Accept() + if err != nil { + t.Fatal("failed to accept") + } + + if !cB.LocalMultiaddr().Equal(maddr) { + t.Fatal("local multiaddr not equal:", maddr, cB.LocalMultiaddr()) + } + + // echo out + buf := make([]byte, 1024) + for { + _, err := cB.Read(buf) + if err != nil { + break + } + cB.Write(buf) + } + + wg.Done() + }() + + cA, err := net.Dial("tcp", "127.0.0.1:4322") + if err != nil { + t.Fatal("failed to dial") + } + + buf := make([]byte, 1024) + if _, err := cA.Write([]byte("beep boop")); err != nil { + t.Fatal("failed to write:", err) + } + + if _, err := cA.Read(buf); err != nil { + t.Fatal("failed to read:", buf, err) + } + + if !bytes.Equal(buf[:9], []byte("beep boop")) { + t.Fatal("failed to echo:", buf) + } + + maddr2, err := FromNetAddr(cA.RemoteAddr()) + if err != nil { + t.Fatal("failed to convert", err) + } + if !maddr2.Equal(maddr) { + t.Fatal("remote multiaddr not equal:", maddr, maddr2) + } + + cA.Close() + wg.Wait() +} + +func TestListenAndDial(t *testing.T) { + + maddr := newMultiaddr(t, "/ip4/127.0.0.1/tcp/4323") + listener, err := Listen(maddr) + if err != nil { + t.Fatal("failed to listen") + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + + cB, err := listener.Accept() + if err != nil { + t.Fatal("failed to accept") + } + + if !cB.LocalMultiaddr().Equal(maddr) { + t.Fatal("local multiaddr not equal:", maddr, cB.LocalMultiaddr()) + } + + // echo out + buf := make([]byte, 1024) + for { + _, err := cB.Read(buf) + if err != nil { + break + } + cB.Write(buf) + } + + wg.Done() + }() + + cA, err := Dial(newMultiaddr(t, "/ip4/127.0.0.1/tcp/4323")) + if err != nil { + t.Fatal("failed to dial") + } + + buf := make([]byte, 1024) + if _, err := cA.Write([]byte("beep boop")); err != nil { + t.Fatal("failed to write:", err) + } + + if _, err := cA.Read(buf); err != nil { + t.Fatal("failed to read:", buf, err) + } + + if !bytes.Equal(buf[:9], []byte("beep boop")) { + t.Fatal("failed to echo:", buf) + } + + maddr2 := cA.RemoteMultiaddr() + if !maddr2.Equal(maddr) { + t.Fatal("remote multiaddr not equal:", maddr, maddr2) + } + + cA.Close() + wg.Wait() +} From a104069971db16023d71dc1e6a2a0d51ea2bb81d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 11 Oct 2014 02:30:03 -0700 Subject: [PATCH 5/5] added travis.yml --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..32ce5a7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: go + +go: + - 1.2 + - 1.3 + - release + - tip + +script: + - go test -v ./...