diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 89c5666..3df340b 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,18 +1,31 @@ { "ImportPath": "github.com/jbenet/go-multiaddr-net", - "GoVersion": "go1.3", + "GoVersion": "go1.4.2", "Packages": [ "./..." ], "Deps": [ { "ImportPath": "github.com/h2so5/utp", - "Rev": "654d875bb65e96729678180215cf080fe2810371" + "Rev": "5288a05e1781334589c4b8806bcfb1e69f5b5d63" + }, + { + "ImportPath": "github.com/jbenet/go-base58", + "Rev": "568a28d73fd97651d3442392036a658b6976eed5" }, { "ImportPath": "github.com/jbenet/go-multiaddr", - "Comment": "0.1.2-34-g0d7b54b", - "Rev": "0d7b54ba432fda14bac37cdad717bd6270eacc85" + "Comment": "0.1.2-38-gc13f11b", + "Rev": "c13f11bbfe6439771f4df7bfb330f686826144e8" + }, + { + "ImportPath": "github.com/jbenet/go-multihash", + "Comment": "0.1.0-36-g87e53a9", + "Rev": "87e53a9d2875a18a7863b351d22f912545e6b3a3" + }, + { + "ImportPath": "golang.org/x/crypto/sha3", + "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d" } ] } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml b/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml index 935bde5..a924a18 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml +++ b/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml @@ -1,7 +1,9 @@ language: go script: - - GO_UTP_LOGGING=2 go test -v -bench . + - GO_UTP_LOGGING=2 go test -v + - GOMAXPROCS=4 GO_UTP_LOGGING=2 go test -v - go test -v -race - GO_UTP_LOGGING=2 go run benchmark/main.go -h + - GOMAXPROCS=4 GO_UTP_LOGGING=2 go run benchmark/main.go -h - GO_UTP_LOGGING=2 cd _ucat_test; make test diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/README.md b/Godeps/_workspace/src/github.com/h2so5/utp/README.md index 8fca2b3..c061f0c 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/README.md +++ b/Godeps/_workspace/src/github.com/h2so5/utp/README.md @@ -3,55 +3,26 @@ utp μTP (Micro Transport Protocol) implementation -[![Build status](https://ci.appveyor.com/api/projects/status/j1be8y7p6nd2wqqw?svg=true)](https://ci.appveyor.com/project/h2so5/utp) -[![Build Status](https://travis-ci.org/h2so5/utp.svg)](https://travis-ci.org/h2so5/utp) +[![Build status](https://ci.appveyor.com/api/projects/status/j1be8y7p6nd2wqqw?svg=true&branch=master)](https://ci.appveyor.com/project/h2so5/utp) +[![Build Status](https://travis-ci.org/h2so5/utp.svg?branch=master)](https://travis-ci.org/h2so5/utp) [![GoDoc](https://godoc.org/github.com/h2so5/utp?status.svg)](http://godoc.org/github.com/h2so5/utp) http://www.bittorrent.org/beps/bep_0029.html -**warning: This is a buggy alpha version.** - -## Benchmark History - -[![Benchmark status](http://107.170.244.57:80/go-utp-bench.php)]() - ## Installation ``` go get github.com/h2so5/utp ``` -## Example +## Debug Log -Echo server +Use GO_UTP_LOGGING to show debug logs. -```go -package main - -import ( - "time" - - "github.com/h2so5/utp" -) - -func main() { - ln, _ := utp.Listen("utp", ":11000") - defer ln.Close() - - conn, _ := ln.AcceptUTP() - conn.SetKeepAlive(time.Minute) - defer conn.Close() - - for { - var buf [1024]byte - l, err := conn.Read(buf[:]) - if err != nil { - break - } - _, err = conn.Write(buf[:l]) - if err != nil { - break - } - } -} +``` +GO_UTP_LOGGING=0 go test <- default, no logging +GO_UTP_LOGGING=1 go test +GO_UTP_LOGGING=2 go test +GO_UTP_LOGGING=3 go test +GO_UTP_LOGGING=4 go test <- most verbose ``` diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/addr.go b/Godeps/_workspace/src/github.com/h2so5/utp/addr.go index bb5af1d..c8ddfb0 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/addr.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/addr.go @@ -2,11 +2,31 @@ package utp import "net" -type UTPAddr struct { +// Addr represents the address of a UTP end point. +type Addr struct { net.Addr } -func (a UTPAddr) Network() string { return "utp" } +// Network returns the address's network name, "utp". +func (a Addr) Network() string { return "utp" } + +// ResolveAddr parses addr as a UTP address of the form "host:port" +// or "[ipv6-host%zone]:port" and resolves a pair of domain name and +// port name on the network net, which must be "utp", "utp4" or +// "utp6". A literal address or host name for IPv6 must be enclosed +// in square brackets, as in "[::1]:80", "[ipv6-host]:http" or +// "[ipv6-host%zone]:80". +func ResolveAddr(n, addr string) (*Addr, error) { + udpnet, err := utp2udp(n) + if err != nil { + return nil, err + } + udp, err := net.ResolveUDPAddr(udpnet, addr) + if err != nil { + return nil, err + } + return &Addr{Addr: udp}, nil +} func utp2udp(n string) (string, error) { switch n { @@ -20,15 +40,3 @@ func utp2udp(n string) (string, error) { return "", net.UnknownNetworkError(n) } } - -func ResolveUTPAddr(n, addr string) (*UTPAddr, error) { - udpnet, err := utp2udp(n) - if err != nil { - return nil, err - } - udp, err := net.ResolveUDPAddr(udpnet, addr) - if err != nil { - return nil, err - } - return &UTPAddr{Addr: udp}, nil -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/base.go b/Godeps/_workspace/src/github.com/h2so5/utp/base.go new file mode 100644 index 0000000..17114c4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/base.go @@ -0,0 +1,315 @@ +package utp + +import ( + "errors" + "net" + "sync" + "sync/atomic" + "syscall" + "time" +) + +var baseConnMap = make(map[string]*baseConn) +var baseConnMutex sync.Mutex + +type packetHandler chan<- *packet + +type baseConn struct { + addr string + conn net.PacketConn + synPackets *packetRingBuffer + outOfBandPackets *packetRingBuffer + + handlers map[uint16]packetHandler + handlerMutex sync.RWMutex + ref int32 + refMutex sync.RWMutex + + rdeadline time.Time + wdeadline time.Time + + softClosed int32 + closed int32 +} + +func newBaseConn(n string, addr *Addr) (*baseConn, error) { + udpnet, err := utp2udp(n) + if err != nil { + return nil, err + } + var s string + if addr != nil { + s = addr.String() + } else { + s = ":0" + } + conn, err := net.ListenPacket(udpnet, s) + if err != nil { + return nil, err + } + c := &baseConn{ + conn: conn, + synPackets: newPacketRingBuffer(packetBufferSize), + outOfBandPackets: newPacketRingBuffer(packetBufferSize), + handlers: make(map[uint16]packetHandler), + } + c.Register(-1, nil) + go c.recvLoop() + return c, nil +} + +func getSharedBaseConn(n string, addr *Addr) (*baseConn, error) { + baseConnMutex.Lock() + defer baseConnMutex.Unlock() + var s string + if addr != nil { + s = addr.String() + } else { + s = ":0" + } + if c, ok := baseConnMap[s]; ok { + return c, nil + } + c, err := newBaseConn(n, addr) + if err != nil { + return nil, err + } + c.addr = s + baseConnMap[s] = c + go c.recvLoop() + return c, nil +} + +func (c *baseConn) ok() bool { return c != nil && c.conn != nil } + +func (c *baseConn) LocalAddr() net.Addr { + if !c.ok() { + return nil + } + return &Addr{Addr: c.conn.LocalAddr()} +} + +func (c *baseConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { + if !c.ok() { + return 0, nil, syscall.EINVAL + } + if !c.isOpen() { + return 0, nil, &net.OpError{ + Op: "read", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errClosing, + } + } + var d time.Duration + if !c.rdeadline.IsZero() { + d = c.rdeadline.Sub(time.Now()) + if d < 0 { + d = 0 + } + } + p, err := c.outOfBandPackets.popOne(d) + if err != nil { + return 0, nil, &net.OpError{ + Op: "read", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: err, + } + } + return copy(b, p.payload), p.addr, nil +} + +func (c *baseConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + if !c.isOpen() { + return 0, &net.OpError{ + Op: "write", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errClosing, + } + } + return c.conn.WriteTo(b, addr) +} + +func (c *baseConn) Close() error { + if !c.ok() { + return syscall.EINVAL + } + if c.isOpen() && atomic.CompareAndSwapInt32(&c.softClosed, 0, 1) { + c.Unregister(-1) + } else { + return &net.OpError{ + Op: "close", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errClosing, + } + } + return nil +} + +func (c *baseConn) SetDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + err := c.SetReadDeadline(t) + if err != nil { + return err + } + return c.SetWriteDeadline(t) +} + +func (c *baseConn) SetReadDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + c.rdeadline = t + return nil +} + +func (c *baseConn) SetWriteDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + c.wdeadline = t + return nil +} + +func (c *baseConn) recvLoop() { + var buf [maxUdpPayload]byte + for { + l, addr, err := c.conn.ReadFrom(buf[:]) + if err != nil { + ulog.Printf(3, "baseConn(%v): %v", c.LocalAddr(), err) + return + } + p, err := c.decodePacket(buf[:l]) + if err != nil { + ulog.Printf(3, "baseConn(%v): RECV out-of-band packet (len: %d) from %v", c.LocalAddr(), l, addr) + c.outOfBandPackets.push(&packet{payload: append([]byte{}, buf[:l]...), addr: addr}) + } else { + p.addr = addr + ulog.Printf(3, "baseConn(%v): RECV: %v from %v", c.LocalAddr(), p, addr) + if p.header.typ == stSyn { + // ignore duplicated syns + if !c.exists(p.header.id + 1) { + c.synPackets.push(p) + } + } else { + c.processPacket(p) + } + } + } +} + +func (c *baseConn) decodePacket(b []byte) (*packet, error) { + var p packet + err := p.UnmarshalBinary(b) + if err != nil { + return nil, err + } + if p.header.ver != version { + return nil, errors.New("unsupported utp version") + } + return &p, nil +} + +func (c *baseConn) exists(id uint16) bool { + c.handlerMutex.RLock() + defer c.handlerMutex.RUnlock() + return c.handlers[id] != nil +} + +func (c *baseConn) processPacket(p *packet) { + c.handlerMutex.RLock() + h, ok := c.handlers[p.header.id] + c.handlerMutex.RUnlock() + if ok { + h <- p + } +} + +func (c *baseConn) Register(id int32, f packetHandler) { + if id < 0 { + c.refMutex.Lock() + c.ref++ + c.refMutex.Unlock() + } else { + if f == nil { + panic("nil handler not allowed") + } + c.handlerMutex.Lock() + _, ok := c.handlers[uint16(id)] + c.handlerMutex.Unlock() + if !ok { + c.refMutex.Lock() + c.ref++ + c.refMutex.Unlock() + c.handlerMutex.Lock() + c.handlers[uint16(id)] = f + c.handlerMutex.Unlock() + ulog.Printf(2, "baseConn(%v): register #%d (ref: %d)", c.LocalAddr(), id, c.ref) + } + } +} + +func (c *baseConn) Unregister(id int32) { + if id < 0 { + c.refMutex.Lock() + c.ref-- + c.refMutex.Unlock() + } else { + c.handlerMutex.Lock() + _, ok := c.handlers[uint16(id)] + c.handlerMutex.Unlock() + if ok { + c.handlerMutex.Lock() + delete(c.handlers, uint16(id)) + c.handlerMutex.Unlock() + c.refMutex.Lock() + c.ref-- + c.refMutex.Unlock() + } + } + c.refMutex.Lock() + r := c.ref + c.refMutex.Unlock() + if r <= 0 { + baseConnMutex.Lock() + defer baseConnMutex.Unlock() + c.close() + delete(baseConnMap, c.addr) + ulog.Printf(2, "baseConn(%v): unregister #%d (ref: %d)", c.LocalAddr(), id, c.ref) + } +} + +func (c *baseConn) close() { + if atomic.CompareAndSwapInt32(&c.closed, 0, 1) { + c.conn.Close() + } +} + +func (c *baseConn) isOpen() bool { + return atomic.LoadInt32(&c.closed) == 0 +} + +func (c *baseConn) Send(p *packet) { + b, err := p.MarshalBinary() + if err != nil { + panic(err) + } + ulog.Printf(3, "baseConn(%v): SEND: %v to %v", c.LocalAddr(), p, p.addr) + _, err = c.conn.WriteTo(b, p.addr) + if err != nil { + ulog.Printf(3, "%v", err) + panic(err) + } +} + +func (c *baseConn) RecvSyn(timeout time.Duration) (*packet, error) { + return c.synPackets.popOne(timeout) +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go new file mode 100644 index 0000000..618dae1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go @@ -0,0 +1,308 @@ +package utp + +import ( + "bytes" + "net" + "sync" + "testing" + "time" +) + +func TestSharedConnRecvPacket(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + ch := make(chan *packet) + c.Register(5, ch) + + for i := 0; i < 100; i++ { + p := &packet{header: header{typ: stData, ver: version, id: 5}} + payload, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + go func() { + uc.Write(payload) + }() + <-ch + } + + c.Unregister(5) +} + +func TestSharedConnSendPacket(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + for i := 0; i < 100; i++ { + addr, err := net.ResolveUDPAddr("udp", uc.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + p := &packet{header: header{typ: stData, ver: version, id: 5}, addr: addr} + payload, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + c.Send(p) + + var b [256]byte + l, err := uc.Read(b[:]) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(b[:l], payload) { + t.Errorf("expected packet of %v; got %v", payload, b[:l]) + } + } +} + +func TestSharedConnRecvSyn(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + for i := 0; i < 100; i++ { + p := &packet{header: header{typ: stSyn, ver: version}} + payload, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + go func() { + uc.Write(payload) + }() + p, err = c.RecvSyn(time.Duration(0)) + if err != nil { + t.Fatal(err) + } + if p == nil { + t.Errorf("packet must not be nil") + } + } +} + +func TestSharedConnRecvOutOfBound(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + for i := 0; i < 100; i++ { + payload := []byte("Hello") + go func() { + uc.Write(payload) + }() + var b [256]byte + l, _, err := c.ReadFrom(b[:]) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(payload, b[:l]) { + t.Errorf("expected packet of %v; got %v", payload, b[:l]) + } + } +} + +func TestSharedConnSendOutOfBound(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + for i := 0; i < 100; i++ { + addr, err := net.ResolveUDPAddr("udp", uc.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + payload := []byte("Hello") + _, err = c.WriteTo(payload, addr) + if err != nil { + t.Fatal(err) + } + + var b [256]byte + l, err := uc.Read(b[:]) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(payload, b[:l]) { + t.Errorf("expected packet of %v; got %v", payload, b[:l]) + } + } +} + +func TestSharedConnReferenceCount(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + var w sync.WaitGroup + + c.Register(-1, nil) + + for i := 0; i < 5; i++ { + w.Add(1) + go func(i int) { + defer w.Done() + c.Register(int32(i), make(chan *packet)) + }(i) + } + + w.Wait() + for i := 0; i < 5; i++ { + w.Add(1) + go func(i int) { + defer w.Done() + c.Unregister(int32(i)) + }(i) + } + + w.Wait() + c.Unregister(-1) + c.Close() + + c = baseConnMap[addr.String()] + if c != nil { + t.Errorf("baseConn should be released", c.ref) + } +} + +func TestSharedConnClose(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + c, err := getSharedBaseConn("utp", addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + for i := 0; i < 5; i++ { + c.Close() + } + + var b [256]byte + _, _, err = c.ReadFrom(b[:]) + if err == nil { + t.Fatal("ReadFrom should fail") + } + + uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + payload := []byte("Hello") + _, err = c.WriteTo(payload, uc.LocalAddr()) + if err == nil { + t.Fatal("WriteTo should fail") + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go b/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go index 830cea0..e59877b 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go @@ -85,25 +85,27 @@ func main() { } func c2s(l int64, stream bool) float64 { - ln, err := utp.Listen("utp", "127.0.0.1:0") + laddr, err := utp.ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + ln, err := utp.Listen("utp", laddr) if err != nil { log.Fatal(err) } - raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String()) - if err != nil { - log.Fatal(err) - } + cch := make(chan *utp.Conn) + go func() { + c, err := utp.DialUTPTimeout("utp", nil, ln.Addr().(*utp.Addr), 1000*time.Millisecond) + if err != nil { + log.Fatal(err) + } - c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond) - if err != nil { - log.Fatal(err) - } - defer c.Close() - - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } + cch <- c + }() s, err := ln.Accept() if err != nil { @@ -112,7 +114,11 @@ func c2s(l int64, stream bool) float64 { defer s.Close() ln.Close() + c := <-cch + defer c.Close() + rch := make(chan int) + wch := make(chan int) sendHash := md5.New() readHash := md5.New() @@ -122,12 +128,13 @@ func c2s(l int64, stream bool) float64 { if stream { go func() { defer c.Close() + defer close(wch) io.Copy(io.MultiWriter(c, sendHash, &counter), io.LimitReader(RandReader{}, l)) }() go func() { + defer close(rch) io.Copy(readHash, s) - close(rch) }() go func() { @@ -148,6 +155,7 @@ func c2s(l int64, stream bool) float64 { start := time.Now() <-rch + <-wch bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) } else { @@ -156,16 +164,18 @@ func c2s(l int64, stream bool) float64 { go func() { defer c.Close() + defer close(wch) io.Copy(c, &sendBuf) }() go func() { + defer close(rch) io.Copy(&readBuf, s) - rch <- 0 }() start := time.Now() <-rch + <-wch bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) io.Copy(sendHash, &sendBuf) @@ -180,25 +190,27 @@ func c2s(l int64, stream bool) float64 { } func s2c(l int64, stream bool) float64 { - ln, err := utp.Listen("utp", "127.0.0.1:0") + laddr, err := utp.ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + ln, err := utp.Listen("utp", laddr) if err != nil { log.Fatal(err) } - raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String()) - if err != nil { - log.Fatal(err) - } + cch := make(chan *utp.Conn) + go func() { + c, err := utp.DialUTPTimeout("utp", nil, ln.Addr().(*utp.Addr), 1000*time.Millisecond) + if err != nil { + log.Fatal(err) + } - c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond) - if err != nil { - log.Fatal(err) - } - defer c.Close() - - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } + cch <- c + }() s, err := ln.Accept() if err != nil { @@ -207,7 +219,11 @@ func s2c(l int64, stream bool) float64 { defer s.Close() ln.Close() + c := <-cch + defer c.Close() + rch := make(chan int) + wch := make(chan int) sendHash := md5.New() readHash := md5.New() @@ -218,12 +234,13 @@ func s2c(l int64, stream bool) float64 { if stream { go func() { defer s.Close() + defer close(wch) io.Copy(io.MultiWriter(s, sendHash, &counter), io.LimitReader(RandReader{}, l)) }() go func() { + defer close(rch) io.Copy(readHash, c) - close(rch) }() go func() { @@ -244,6 +261,7 @@ func s2c(l int64, stream bool) float64 { start := time.Now() <-rch + <-wch bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) } else { @@ -252,16 +270,18 @@ func s2c(l int64, stream bool) float64 { go func() { defer s.Close() + defer close(wch) io.Copy(s, &sendBuf) }() go func() { + defer close(rch) io.Copy(&readBuf, c) - rch <- 0 }() start := time.Now() <-rch + <-wch bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) io.Copy(sendHash, &sendBuf) diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go b/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go index d889b5b..830891f 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go @@ -2,7 +2,9 @@ package utp import ( "errors" + "io" "math" + "sync" "time" ) @@ -74,7 +76,7 @@ func (b *packetBuffer) compact() { } } -func (b *packetBuffer) first() *packet { +func (b *packetBuffer) front() *packet { if b.root == nil || b.root.p == nil { return nil } @@ -157,6 +159,9 @@ func (b *packetBuffer) generateSelectiveACK() []byte { ack = ack[:len(ack)-1] } + if len(ack) == 0 { + return nil + } return ack } @@ -185,46 +190,280 @@ func (b *packetBuffer) processSelectiveACK(ack []byte) { } } -type timedBuffer struct { - d time.Duration - root *timedBufferNode +type packetRingBuffer struct { + b []*packet + begin int + s int + mutex sync.RWMutex + rch chan int } -type timedBufferNode struct { - val float64 - next *timedBufferNode - pushed time.Time +func newPacketRingBuffer(s int) *packetRingBuffer { + return &packetRingBuffer{ + b: make([]*packet, s), + rch: make(chan int), + } } -func (b *timedBuffer) push(val float64) { - var before *timedBufferNode - for n := b.root; n != nil; n = n.next { - if time.Now().Sub(n.pushed) >= b.d { - if before != nil { - before.next = nil - } else { - b.root = nil +func (b *packetRingBuffer) size() int { + b.mutex.RLock() + defer b.mutex.RUnlock() + return b.s +} + +func (b *packetRingBuffer) empty() bool { + return b.size() == 0 +} + +func (b *packetRingBuffer) push(p *packet) { + b.mutex.Lock() + defer b.mutex.Unlock() + b.b[(b.begin+b.s)%len(b.b)] = p + if b.s < len(b.b) { + b.s++ + } else { + b.begin = (b.begin + 1) % len(b.b) + } + select { + case b.rch <- 0: + default: + } +} + +func (b *packetRingBuffer) pop() *packet { + if b.empty() { + return nil + } + b.mutex.Lock() + defer b.mutex.Unlock() + p := b.b[b.begin] + b.begin = (b.begin + 1) % len(b.b) + b.s-- + return p +} + +func (b *packetRingBuffer) popOne(timeout time.Duration) (*packet, error) { + var t <-chan time.Time + if timeout != 0 { + t = time.After(timeout) + } + if b.empty() { + select { + case <-b.rch: + case <-t: + return nil, errTimeout + } + } + return b.pop(), nil +} + +type byteRingBuffer struct { + b []byte + begin int + s int + mutex sync.RWMutex + rch chan int + closech chan int + closechMutex sync.Mutex +} + +func newByteRingBuffer(s int) *byteRingBuffer { + return &byteRingBuffer{ + b: make([]byte, s), + rch: make(chan int), + closech: make(chan int), + } +} + +func (r *byteRingBuffer) size() int { + r.mutex.RLock() + defer r.mutex.RUnlock() + return r.s +} + +func (r *byteRingBuffer) space() int { + r.mutex.RLock() + defer r.mutex.RUnlock() + return len(r.b) - r.s +} + +func (r *byteRingBuffer) empty() bool { + return r.size() == 0 +} + +func (r *byteRingBuffer) Write(b []byte) (int, error) { + r.mutex.Lock() + defer r.mutex.Unlock() + + for len(b) > 0 { + end := (r.begin + r.s) % len(r.b) + n := copy(r.b[end:], b) + b = b[n:] + + s := r.s + n + if s > len(r.b) { + r.begin = (r.begin + s - len(r.b)) % len(r.b) + r.s = len(r.b) + } else { + r.s += n + } + } + select { + case r.rch <- 0: + case <-r.closech: + return 0, io.EOF + default: + } + return len(b), nil +} + +func (r *byteRingBuffer) ReadTimeout(b []byte, timeout time.Duration) (int, error) { + var t <-chan time.Time + if timeout != 0 { + t = time.After(timeout) + } + if r.empty() { + select { + case <-r.rch: + case <-t: + return 0, errTimeout + case <-r.closech: + return 0, io.EOF + } + } + l := r.size() + if l > len(b) { + l = len(b) + } + r.mutex.Lock() + defer r.mutex.Unlock() + if r.begin+l > len(r.b) { + n := copy(b, r.b[r.begin:]) + n = copy(b[n:], r.b[:]) + r.begin = n + } else { + copy(b, r.b[r.begin:r.begin+l]) + r.begin = (r.begin + l) % len(r.b) + } + r.s -= l + return l, nil +} + +func (r *byteRingBuffer) Close() error { + r.closechMutex.Lock() + defer r.closechMutex.Unlock() + select { + case <-r.closech: + return errClosing + default: + close(r.closech) + } + return nil +} + +type rateLimitedBuffer struct { + wch chan<- []byte + closech chan int + closechMutex sync.Mutex + size uint32 + sizech chan uint32 + sizeMutex sync.Mutex +} + +func newRateLimitedBuffer(ch chan<- []byte, size uint32) *rateLimitedBuffer { + return &rateLimitedBuffer{ + wch: ch, + closech: make(chan int), + size: size, + sizech: make(chan uint32), + } +} + +func (r *rateLimitedBuffer) WriteTimeout(b []byte, timeout time.Duration) (int, error) { + var t <-chan time.Time + if timeout != 0 { + t = time.After(timeout) + } + + for wrote := uint32(0); wrote < uint32(len(b)); { + r.sizeMutex.Lock() + s := r.size + r.sizeMutex.Unlock() + if s == 0 { + select { + case ns := <-r.sizech: + s = ns + case <-r.closech: + return 0, errClosing } - break } - before = n + if s > uint32(len(b))-wrote { + s = uint32(len(b)) - wrote + } + select { + case r.wch <- append([]byte{}, b[wrote:wrote+s]...): + wrote += s + r.sizeMutex.Lock() + r.size -= uint32(s) + r.sizeMutex.Unlock() + case <-r.closech: + return 0, errClosing + case <-t: + return 0, errTimeout + } } - b.root = &timedBufferNode{ - val: val, - next: b.root, - pushed: time.Now(), + + return len(b), nil +} + +func (r *rateLimitedBuffer) Reset(size uint32) { + r.sizeMutex.Lock() + defer r.sizeMutex.Unlock() + r.size = size + select { + case r.sizech <- size: + default: } } -func (b *timedBuffer) min() float64 { - if b.root == nil { - return 0 +func (r *rateLimitedBuffer) Close() error { + r.closechMutex.Lock() + defer r.closechMutex.Unlock() + select { + case <-r.closech: + return errClosing + default: + close(r.closech) } - min := b.root.val - for n := b.root; n != nil; n = n.next { - if min > n.val { - min = n.val + return nil +} + +type baseDelayBuffer struct { + b [6]uint32 + last int + min uint32 +} + +func (b *baseDelayBuffer) Push(val uint32) { + t := time.Now() + i := t.Second()/20 + (t.Minute()%2)*3 + if b.last == i { + if b.b[i] > val { + b.b[i] = val + } + } else { + b.b[i] = val + b.last = i + } + min := val + for _, v := range b.b { + if v > 0 && min > v { + min = v } } - return min + b.min = min +} + +func (b *baseDelayBuffer) Min() uint32 { + return b.min } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go new file mode 100644 index 0000000..8249473 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go @@ -0,0 +1,177 @@ +package utp + +import ( + "bytes" + "math" + "testing" + "time" +) + +func TestPacketBuffer(t *testing.T) { + size := 12 + b := newPacketBuffer(12, 1) + + if b.space() != size { + t.Errorf("expected space == %d; got %d", size, b.space()) + } + + for i := 1; i <= size; i++ { + b.push(&packet{header: header{seq: uint16(i)}}) + } + + if b.space() != 0 { + t.Errorf("expected space == 0; got %d", b.space()) + } + + a := []byte{255, 7} + ack := b.generateSelectiveACK() + if !bytes.Equal(a, ack) { + t.Errorf("expected ack == %v; got %v", a, ack) + } + + err := b.push(&packet{header: header{seq: 15}}) + if err == nil { + t.Fatal("push should fail") + } + + all := b.all() + if len(all) != size { + t.Errorf("expected %d packets sequence; got %d", size, len(all)) + } + + f := b.fetch(6) + if f == nil { + t.Fatal("fetch should not fail") + } + + b.compact() + + err = b.push(&packet{header: header{seq: 15}}) + if err != nil { + t.Fatal(err) + } + + err = b.push(&packet{header: header{seq: 17}}) + if err != nil { + t.Fatal(err) + } + + for i := 7; i <= size; i++ { + f := b.fetch(uint16(i)) + if f == nil { + t.Fatal("fetch should not fail") + } + } + + a = []byte{128, 2} + ack = b.generateSelectiveACK() + if !bytes.Equal(a, ack) { + t.Errorf("expected ack == %v; got %v", a, ack) + } + + all = b.all() + if len(all) != 2 { + t.Errorf("expected 2 packets sequence; got %d", len(all)) + } + + b.compact() + if b.space() != 9 { + t.Errorf("expected space == 9; got %d", b.space()) + } + + ack = b.generateSelectiveACK() + b.processSelectiveACK(ack) + + all = b.all() + if len(all) != 1 { + t.Errorf("expected size == 1; got %d", len(all)) + } +} + +func TestPacketBufferBoundary(t *testing.T) { + begin := math.MaxUint16 - 3 + b := newPacketBuffer(12, begin) + for i := begin; i != 5; i = (i + 1) % (math.MaxUint16 + 1) { + err := b.push(&packet{header: header{seq: uint16(i)}}) + if err != nil { + t.Fatal(err) + } + } +} + +func TestPacketRingBuffer(t *testing.T) { + b := newPacketRingBuffer(5) + for i := 0; i < 7; i++ { + b.push(&packet{header: header{seq: uint16(i)}}) + } + + if b.size() != 5 { + t.Errorf("expected size == 5; got %d", b.size()) + } + + p := b.pop() + if p.header.seq != 2 { + t.Errorf("expected header.seq == 2; got %d", p.header.seq) + } + + if b.size() != 4 { + t.Errorf("expected size == 4; got %d", b.size()) + } + + for b.pop() != nil {} + + if !b.empty() { + t.Errorf("buffer must be empty") + } + + go func() { + for i := 0; i < 5; i++ { + b.push(&packet{header: header{seq: uint16(i)}}) + } + }() + + p, err := b.popOne(time.Second) + if err != nil { + t.Fatal(err) + } + + if p.header.seq != 0 { + t.Errorf("expected header.seq == 0; got %d", p.header.seq) + } +} + +func TestByteRingBuffer(t *testing.T) { + + b := newByteRingBuffer(5) + for i := 0; i < 100; i++ { + b.Write([]byte{byte(i)}) + } + + var buf [10]byte + l, err := b.ReadTimeout(buf[:], 0) + if err != nil { + t.Fatal(err) + } + + e := []byte{95, 96, 97, 98, 99} + if !bytes.Equal(buf[:l], e) { + t.Errorf("expected payload of %v; got %v", e, buf[:l]) + } + + e2 := []byte("abcdefghijklmnopqrstuvwxyz") + go func() { + _, err := b.Write(e2) + if err != nil { + t.Fatal(err) + } + }() + + l, err = b.ReadTimeout(buf[:], 0) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf[:l], e2[len(e2)-5:]) { + t.Errorf("expected payload of %v; got %v", e2[len(e2)-5:], buf[:l]) + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/conn.go b/Godeps/_workspace/src/github.com/h2so5/utp/conn.go index ee41e97..b1544e9 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/conn.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/conn.go @@ -1,48 +1,55 @@ package utp import ( - "bytes" - "errors" - "io" "math" - "math/rand" "net" + "sync" + "sync/atomic" "syscall" "time" ) -type UTPConn struct { - conn net.PacketConn - raddr net.Addr - rid, sid, seq, ack, lastAck uint16 - rtt, rttVar, minRtt, rto, dupAck int64 - diff, maxWindow uint32 - rdeadline, wdeadline time.Time +// Conn is an implementation of the Conn interface for UTP network +// connections. +type Conn struct { + conn *baseConn + raddr net.Addr + rid, sid, seq, ack, lastAck uint16 + rtt, rttVar, minRtt, rto int64 + dupAck int + diff, maxWindow uint32 - state state - lastTimedOut time.Time + state int + closed int32 - outch chan *outgoingPacket - outchch chan int - sendch chan *outgoingPacket - sendchch chan int - recvch chan *packet - recvchch chan int - readch chan []byte - readchch chan int - winch chan uint32 - quitch chan int - activech chan int - connch chan error - finch chan int - closech chan<- uint16 - eofid uint16 - keepalivech chan time.Duration - - readbuf bytes.Buffer recvbuf *packetBuffer sendbuf *packetBuffer + readbuf *byteRingBuffer + writebuf *rateLimitedBuffer + + baseDelay baseDelayBuffer + + writech chan []byte + ackch chan int + synch chan int + + rdeadline time.Time + wdeadline time.Time + deadlineMutex sync.RWMutex + + recv chan *packet + + closing bool + closingch chan int + + keepalivech chan time.Duration + + connch chan int + + closech chan int + closechMutex sync.Mutex + stat statistics } @@ -54,351 +61,229 @@ type statistics struct { packetTimedOuts int sentSelectiveACKs int receivedSelectiveACKs int - - rtoSum int - rtoCount int + rtoSum int64 + rtoCount int } -func dial(n string, laddr, raddr *UTPAddr, timeout time.Duration) (*UTPConn, error) { - udpnet, err := utp2udp(n) - if err != nil { - return nil, err - } - - // TODO extract - if laddr == nil { - addr, err := net.ResolveUDPAddr(udpnet, ":0") - if err != nil { - return nil, err - } - laddr = &UTPAddr{Addr: addr} - } - - conn, err := net.ListenPacket(udpnet, laddr.Addr.String()) - if err != nil { - return nil, err - } - - id := uint16(rand.Intn(math.MaxUint16)) - - c := newUTPConn() - c.conn = conn - c.raddr = raddr.Addr - c.rid = id - c.sid = id + 1 - c.seq = 1 - c.state = state_syn_sent - c.sendbuf = newPacketBuffer(window_size, 1) - - go c.recv() - go c.loop() - - select { - case c.sendch <- &outgoingPacket{st_syn, nil, nil}: - case <-c.sendchch: - return nil, errors.New("use of closed network connection") - } - - var t <-chan time.Time - if timeout != 0 { - t = time.After(timeout) - } - - select { - case err := <-c.connch: - if err != nil { - c.closed() - return nil, err - } - ulog.Printf(1, "Conn(%v): Connected", c.LocalAddr()) - return c, nil - case <-t: - c.quitch <- 0 - return nil, &timeoutError{} - } -} - -func newUTPConn() *UTPConn { - rto := 60 - - return &UTPConn{ +func newConn() *Conn { + wch := make(chan []byte) + c := &Conn{ minRtt: math.MaxInt64, - maxWindow: mtu, - rto: int64(rto), + maxWindow: mss, + rto: int64(60), - outch: make(chan *outgoingPacket, 1), - outchch: make(chan int), - sendch: make(chan *outgoingPacket, 1), - sendchch: make(chan int), - recvch: make(chan *packet, 2), - recvchch: make(chan int), - winch: make(chan uint32, 1), - quitch: make(chan int), - activech: make(chan int), - readch: make(chan []byte, 1), - readchch: make(chan int), - connch: make(chan error, 1), - finch: make(chan int, 1), + recv: make(chan *packet), + connch: make(chan int), + recvbuf: newPacketBuffer(0, 0), + + readbuf: newByteRingBuffer(readBufferSize), + writebuf: newRateLimitedBuffer(wch, mss), + + writech: wch, + ackch: make(chan int), + synch: make(chan int), + + closingch: make(chan int), keepalivech: make(chan time.Duration), - - stat: statistics{ - rtoSum: rto, - rtoCount: 1, - }, + closech: make(chan int), } + return c } -func (c *UTPConn) ok() bool { return c != nil && c.conn != nil } +func (c *Conn) ok() bool { return c != nil && c.conn != nil } -func (c *UTPConn) Close() error { +// Close closes the connection. +func (c *Conn) Close() error { if !c.ok() { return syscall.EINVAL } - + if !c.isOpen() { + return nil + } select { - case <-c.activech: + case <-c.closingch: default: - c.quitch <- 0 - ulog.Printf(2, "Conn(%v): Wait for close", c.LocalAddr()) - <-c.finch + close(c.closingch) } - + select { + case <-c.connch: + default: + return nil + } + <-c.closech return nil } -func (c *UTPConn) LocalAddr() net.Addr { - return &UTPAddr{Addr: c.conn.LocalAddr()} +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + if !c.ok() { + return nil + } + return c.conn.LocalAddr() } -func (c *UTPConn) RemoteAddr() net.Addr { - return &UTPAddr{Addr: c.raddr} +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + if !c.ok() { + return nil + } + return c.raddr } -func (c *UTPConn) Read(b []byte) (int, error) { +// Read implements the Conn Read method. +func (c *Conn) Read(b []byte) (int, error) { if !c.ok() { return 0, syscall.EINVAL } - - if c.readbuf.Len() == 0 { - var timeout <-chan time.Time - if !c.rdeadline.IsZero() { - timeout = time.After(c.rdeadline.Sub(time.Now())) - } - - select { - case b := <-c.readch: - if b == nil { - return 0, io.EOF - } - _, err := c.readbuf.Write(b) - if err != nil { - return 0, err - } - case <-c.readchch: - loop: - for { - select { - case b := <-c.readch: - _, err := c.readbuf.Write(b) - if err != nil { - return 0, err - } - default: - break loop - } - } - if c.readbuf.Len() == 0 { - return 0, io.EOF - } - case <-timeout: - return 0, &timeoutError{} + if !c.isOpen() { + return 0, &net.OpError{ + Op: "read", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errClosing, } } - return c.readbuf.Read(b) + s := c.readbuf.space() + c.deadlineMutex.RLock() + d := timeToDeadline(c.rdeadline) + c.deadlineMutex.RUnlock() + l, err := c.readbuf.ReadTimeout(b, d) + if s < mss && c.readbuf.space() > 0 { + select { + case c.ackch <- 0: + default: + } + } + return l, err } -func (c *UTPConn) Write(b []byte) (int, error) { +func timeToDeadline(deadline time.Time) (d time.Duration) { + if deadline.IsZero() { + return + } + d = deadline.Sub(time.Now()) + if d < 0 { + d = 0 + } + return +} + +// Write implements the Conn Write method. +func (c *Conn) Write(b []byte) (int, error) { if !c.ok() { return 0, syscall.EINVAL } - - var wrote uint64 - for { - l := uint64(len(b)) - wrote - if l > mss { - l = mss - } - select { - case c.outch <- &outgoingPacket{st_data, nil, b[wrote : wrote+l]}: - case <-c.outchch: - return 0, errors.New("use of closed network connection") - } - - wrote += l - ulog.Printf(4, "Conn(%v): Write %d/%d bytes", c.LocalAddr(), wrote, len(b)) - if l < mss { - break + if !c.isOpen() { + return 0, &net.OpError{ + Op: "write", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errClosing, } } - - return len(b), nil + c.deadlineMutex.RLock() + d := timeToDeadline(c.wdeadline) + c.deadlineMutex.RUnlock() + return c.writebuf.WriteTimeout(b, d) } -func (c *UTPConn) SetDeadline(t time.Time) error { +// SetDeadline implements the Conn SetDeadline method. +func (c *Conn) SetDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.SetReadDeadline(t); err != nil { + err := c.SetReadDeadline(t) + if err != nil { return err } - if err := c.SetWriteDeadline(t); err != nil { - return err - } - return nil + return c.SetWriteDeadline(t) } -func (c *UTPConn) SetReadDeadline(t time.Time) error { +// SetReadDeadline implements the Conn SetReadDeadline method. +func (c *Conn) SetReadDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } + c.deadlineMutex.Lock() + defer c.deadlineMutex.Unlock() c.rdeadline = t return nil } -func (c *UTPConn) SetWriteDeadline(t time.Time) error { +// SetWriteDeadline implements the Conn SetWriteDeadline method. +func (c *Conn) SetWriteDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } + c.deadlineMutex.Lock() + defer c.deadlineMutex.Unlock() c.wdeadline = t return nil } -func (c *UTPConn) SetKeepAlive(d time.Duration) error { +// SetKeepAlive sets the keepalive interval associated with the connection. +func (c *Conn) SetKeepAlive(d time.Duration) error { if !c.ok() { return syscall.EINVAL } - select { - case <-c.activech: - default: - c.keepalivech <- d + if !c.isOpen() { + return errClosing } + c.keepalivech <- d return nil } -func readPacket(data []byte) (*packet, error) { - p := globalPool.get() - err := p.UnmarshalBinary(data) - if err != nil { - return nil, err - } - if p.header.ver != version { - return nil, errors.New("unsupported header version") - } - return p, nil -} +func (c *Conn) loop() { + defer c.conn.Unregister(int32(c.rid)) -func (c *UTPConn) recv() { - for { - var buf [mtu]byte - len, addr, err := c.conn.ReadFrom(buf[:]) - if err != nil { - return - } - if addr.String() != c.raddr.String() { - continue - } - p, err := readPacket(buf[:len]) - if err == nil { - select { - case c.recvch <- p: - case <-c.recvchch: - return - } - } - } -} - -func (c *UTPConn) loop() { - var recvExit, sendExit bool - var lastReceived time.Time + var resendSeq uint16 + var resendCont int var keepalive <-chan time.Time - go func() { - var window uint32 = window_size * mtu - for { - if window >= mtu { - select { - case b := <-c.outch: - select { - case c.sendch <- b: - window -= mtu - case <-c.sendchch: - return - } - case <-c.outchch: - return - case w := <-c.winch: - window = w - } - } else { - window = <-c.winch - } - } - }() - for { - select { - case <-c.sendchch: - sendExit = true - default: + f := c.sendbuf.front() + var resend <-chan time.Time + if f != nil { + resend = time.After(time.Duration(c.rto) * time.Millisecond) } select { - case <-c.recvchch: - recvExit = true - default: - } - select { - case p := <-c.recvch: - ack := c.processPacket(p) - lastReceived = time.Now() - if ack { - out := &outgoingPacket{st_state, nil, nil} - selack := c.sendbuf.generateSelectiveACK() - if len(selack) > 0 { - out.ext = []extension{ - extension{ - typ: ext_selective_ack, - payload: selack, - }, - } - c.stat.sentSelectiveACKs++ - } - c.sendPacket(out) + case <-c.ackch: + c.sendACK() + case <-c.synch: + c.sendSYN() + case p := <-c.recv: + c.stat.receivedPackets++ + c.processPacket(p) + case b := <-c.writech: + c.sendDATA(b) + case <-c.closingch: + c.enterClosing() + case <-resend: + if resendSeq == f.header.seq { + resendCont++ + } else { + resendCont = 0 + resendSeq = f.header.seq } - - case b := <-c.sendch: - c.sendPacket(b) - - case <-time.After(time.Duration(c.rto) * time.Millisecond): - if !c.state.active && time.Now().Sub(lastReceived) > reset_timeout { - ulog.Printf(2, "Conn(%v): Connection timed out", c.LocalAddr()) - c.sendPacket(&outgoingPacket{st_reset, nil, nil}) + c.stat.packetTimedOuts++ + if resendCont > maxRetry { + c.sendRST() c.close() } else { - t, err := c.sendbuf.frontPushedTime() - if err == nil && c.lastTimedOut != t && time.Now().Sub(t) > time.Duration(c.rto)*time.Millisecond { - c.lastTimedOut = t - c.stat.packetTimedOuts++ - c.maxWindow /= 2 - if c.maxWindow < mtu { - c.maxWindow = mtu - } - for _, p := range c.sendbuf.sequence() { - c.resendPacket(p) - } + c.maxWindow /= 2 + if c.maxWindow < mtu { + c.maxWindow = mtu + } + for _, p := range c.sendbuf.sequence() { + c.resend(p) } } + case <-c.closech: + c.readbuf.Close() + c.state = stateClosed + atomic.StoreInt32(&c.closed, 1) + return case d := <-c.keepalivech: if d <= 0 { keepalive = nil @@ -407,56 +292,55 @@ func (c *UTPConn) loop() { } case <-keepalive: ulog.Printf(2, "Conn(%v): Send keepalive", c.LocalAddr()) - c.sendPacket(&outgoingPacket{st_state, nil, nil}) - - case <-c.quitch: - if c.state.exit != nil { - c.state.exit(c) + c.sendACK() + } + if c.closing { + if c.state == stateSynSent || (c.recvbuf.empty() && c.sendbuf.empty()) { + c.close() } } - if recvExit && sendExit { - return - } } } -func (c *UTPConn) sendPacket(b *outgoingPacket) { - p := c.makePacket(b) - bin, err := p.MarshalBinary() - if err == nil { - ulog.Printf(3, "SEND %v -> %v: %v", c.conn.LocalAddr(), c.raddr, p.String()) - c.stat.sentPackets++ - _, err = c.conn.WriteTo(bin, c.raddr) - if err != nil { - return - } - if b.typ != st_state { - c.sendbuf.push(p) - } else { - globalPool.put(p) - } +func (c *Conn) enterClosing() { + if !c.closing { + c.sendFIN() + c.writebuf.Close() + c.state = stateFinSent + c.closing = true } } -func (c *UTPConn) resendPacket(p *packet) { - bin, err := p.MarshalBinary() - if err == nil { - ulog.Printf(3, "RESEND %v -> %v: %v", c.conn.LocalAddr(), c.raddr, p.String()) - c.stat.resentPackets++ - _, err = c.conn.WriteTo(bin, c.raddr) - if err != nil { - return - } +func (c *Conn) close() { + c.closechMutex.Lock() + defer c.closechMutex.Unlock() + select { + case <-c.closech: + default: + close(c.closech) } + ulog.Printf(1, "Conn(%v): closed", c.LocalAddr()) + ulog.Printf(1, "Conn(%v): * SentPackets: %d", c.LocalAddr(), c.stat.sentPackets) + ulog.Printf(1, "Conn(%v): * ResentPackets: %d", c.LocalAddr(), c.stat.resentPackets) + ulog.Printf(1, "Conn(%v): * ReceivedPackets: %d", c.LocalAddr(), c.stat.receivedPackets) + ulog.Printf(1, "Conn(%v): * ReceivedDuplicatedACKs: %d", c.LocalAddr(), c.stat.receivedDuplicatedACKs) + ulog.Printf(1, "Conn(%v): * PacketTimedOuts: %d", c.LocalAddr(), c.stat.packetTimedOuts) + ulog.Printf(1, "Conn(%v): * SentSelectiveACKs: %d", c.LocalAddr(), c.stat.sentSelectiveACKs) + ulog.Printf(1, "Conn(%v): * ReceivedSelectiveACKs: %d", c.LocalAddr(), c.stat.receivedSelectiveACKs) + if c.stat.rtoCount > 0 { + ulog.Printf(1, "Conn(%v): * AverageRTO: %d", c.LocalAddr(), c.stat.rtoSum/int64(c.stat.rtoCount)) + } +} + +func (c *Conn) isOpen() bool { + return atomic.LoadInt32(&c.closed) == 0 } func currentMicrosecond() uint32 { return uint32(time.Now().Nanosecond() / 1000) } -func (c *UTPConn) processPacket(p *packet) bool { - var ack bool - +func (c *Conn) processPacket(p *packet) { if p.header.t == 0 { c.diff = 0 } else { @@ -469,15 +353,14 @@ func (c *UTPConn) processPacket(p *packet) bool { } } - ulog.Printf(3, "RECV %v -> %v: %v", c.raddr, c.conn.LocalAddr(), p.String()) - c.stat.receivedPackets++ + c.baseDelay.Push(c.diff) - if p.header.typ == st_state { - - f := c.sendbuf.first() + switch p.header.typ { + case stState: + f := c.sendbuf.front() if f != nil && p.header.ack == f.header.seq { for _, e := range p.ext { - if e.typ == ext_selective_ack { + if e.typ == extSelectiveAck { ulog.Printf(3, "Conn(%v): Receive Selective ACK", c.LocalAddr()) c.stat.receivedSelectiveACKs++ c.sendbuf.processSelectiveACK(e.payload) @@ -507,12 +390,12 @@ func (c *UTPConn) processPacket(p *packet) bool { } else if c.rto > 1000 { c.rto = 1000 } - c.stat.rtoSum += int(c.rto) + c.stat.rtoSum += c.rto c.stat.rtoCount++ } - if c.diff != 0 { - ourDelay := float64(c.diff) + ourDelay := float64(c.diff - c.baseDelay.Min()) + if ourDelay != 0.0 { offTarget := 100000.0 - ourDelay windowFactor := float64(mtu) / float64(c.maxWindow) delayFactor := offTarget / 100000.0 @@ -523,85 +406,139 @@ func (c *UTPConn) processPacket(p *packet) bool { } ulog.Printf(4, "Conn(%v): Update maxWindow: %d", c.LocalAddr(), c.maxWindow) } - globalPool.put(s) } + c.sendbuf.compact() + if c.lastAck == p.header.ack { c.dupAck++ if c.dupAck >= 2 { - ulog.Printf(3, "Conn(%v): Receive 3 duplicated acks: %d", c.LocalAddr(), p.header.ack) c.stat.receivedDuplicatedACKs++ - p := c.sendbuf.first() + ulog.Printf(3, "Conn(%v): Receive 3 duplicated acks: %d", c.LocalAddr(), p.header.ack) + p := c.sendbuf.front() if p != nil { c.maxWindow /= 2 if c.maxWindow < mtu { c.maxWindow = mtu } ulog.Printf(4, "Conn(%v): Update maxWindow: %d", c.LocalAddr(), c.maxWindow) - c.resendPacket(p) + c.resend(p) } c.dupAck = 0 } } else { c.dupAck = 0 } + c.lastAck = p.header.ack if p.header.ack == c.seq-1 { wnd := p.header.wnd if wnd > c.maxWindow { wnd = c.maxWindow } - ulog.Printf(4, "Conn(%v): Reset window: %d", c.LocalAddr(), wnd) - go func() { - c.winch <- wnd - }() + c.writebuf.Reset(wnd) } - if c.state.state != nil { - c.state.state(c, p) + + if c.state == stateSynSent { + c.recvbuf = newPacketBuffer(windowSize, int(p.header.seq)) + c.state = stateConnected + close(c.connch) } - globalPool.put(p) - } else if p.header.typ == st_reset { - globalPool.put(p) + + case stReset: + c.sendRST() c.close() - } else { - if c.recvbuf == nil { - return false - } - ack = true + + default: c.recvbuf.push(p) for _, s := range c.recvbuf.fetchSequence() { c.ack = s.header.seq - switch s.header.typ { - case st_data: - if c.state.data != nil { - c.state.data(c, s) - } - case st_fin: - if c.state.fin != nil { - c.state.fin(c, s) - } - case st_state: - if c.state.state != nil { - c.state.state(c, s) - } + if s.header.typ == stData { + c.readbuf.Write(s.payload) + } else if s.header.typ == stFin { + c.enterClosing() } - globalPool.put(s) } + c.sendACK() } - return ack } -func (c *UTPConn) makePacket(b *outgoingPacket) *packet { - wnd := window_size * mtu +func (c *Conn) sendACK() { + ack := c.makePacket(stState, nil, c.raddr) + selack := c.sendbuf.generateSelectiveACK() + if selack != nil { + c.stat.sentSelectiveACKs++ + ack.ext = []extension{ + extension{ + typ: extSelectiveAck, + payload: selack, + }, + } + } + c.stat.sentPackets++ + c.conn.Send(ack) +} + +func (c *Conn) sendSYN() { + syn := c.makePacket(stSyn, nil, c.raddr) + err := c.sendbuf.push(syn) + if err != nil { + panic(err) + } + c.stat.sentPackets++ + c.conn.Send(syn) +} + +func (c *Conn) sendFIN() { + fin := c.makePacket(stFin, nil, c.raddr) + err := c.sendbuf.push(fin) + if err != nil { + panic(err) + } + c.stat.sentPackets++ + c.conn.Send(fin) +} + +func (c *Conn) sendRST() { + rst := c.makePacket(stReset, nil, c.raddr) + c.stat.sentPackets++ + c.conn.Send(rst) +} + +func (c *Conn) sendDATA(b []byte) { + for i := 0; i <= len(b)/mss; i++ { + l := len(b) - i*mss + if l > mss { + l = mss + } + data := c.makePacket(stData, b[i*mss:i*mss+l], c.raddr) + c.sendbuf.push(data) + c.stat.sentPackets++ + c.conn.Send(data) + } +} + +func (c *Conn) resend(p *packet) { + c.stat.resentPackets++ + c.conn.Send(p) + ulog.Printf(3, "Conn(%v): RESEND: %s", c.LocalAddr(), p.String()) +} + +func (c *Conn) makePacket(typ int, payload []byte, dst net.Addr) *packet { + wnd := windowSize * mtu if c.recvbuf != nil { wnd = c.recvbuf.space() * mtu } + s := c.readbuf.space() + if wnd > s { + wnd = s + } id := c.sid - if b.typ == st_syn { + if typ == stSyn { id = c.rid } - p := globalPool.get() - p.header.typ = b.typ + p := &packet{} + p.header.typ = typ p.header.ver = version p.header.id = id p.header.t = currentMicrosecond() @@ -609,153 +546,10 @@ func (c *UTPConn) makePacket(b *outgoingPacket) *packet { p.header.wnd = uint32(wnd) p.header.seq = c.seq p.header.ack = c.ack - if b.typ == st_fin { - c.eofid = c.seq - } - if !(b.typ == st_state && len(b.payload) == 0) { + p.addr = dst + if typ != stState { c.seq++ } - p.payload = p.payload[:len(b.payload)] - copy(p.payload, b.payload) + p.payload = payload return p } - -func (c *UTPConn) close() { - if !c.state.closed { - close(c.outchch) - close(c.readchch) - close(c.sendchch) - close(c.recvchch) - close(c.activech) - close(c.finch) - c.closed() - - // Accepted connection - if c.closech != nil { - c.closech <- c.sid - } else { - c.conn.Close() - } - - ulog.Printf(1, "Conn(%v): Closed", c.LocalAddr()) - ulog.Printf(1, "Conn(%v): * SentPackets: %d", c.LocalAddr(), c.stat.sentPackets) - ulog.Printf(1, "Conn(%v): * ResentPackets: %d", c.LocalAddr(), c.stat.resentPackets) - ulog.Printf(1, "Conn(%v): * ReceivedPackets: %d", c.LocalAddr(), c.stat.receivedPackets) - ulog.Printf(1, "Conn(%v): * ReceivedDuplicatedACKs: %d", c.LocalAddr(), c.stat.receivedDuplicatedACKs) - ulog.Printf(1, "Conn(%v): * PacketTimedOuts: %d", c.LocalAddr(), c.stat.packetTimedOuts) - ulog.Printf(1, "Conn(%v): * SentSelectiveACKs: %d", c.LocalAddr(), c.stat.sentSelectiveACKs) - ulog.Printf(1, "Conn(%v): * ReceivedSelectiveACKs: %d", c.LocalAddr(), c.stat.receivedSelectiveACKs) - ulog.Printf(1, "Conn(%v): * AverageRTO: %d", c.LocalAddr(), c.stat.rtoSum/c.stat.rtoCount) - } -} - -func (c *UTPConn) closed() { - ulog.Printf(2, "Conn(%v): Change state: CLOSED", c.LocalAddr()) - c.state = state_closed -} - -func (c *UTPConn) closing() { - ulog.Printf(2, "Conn(%v): Change state: CLOSING", c.LocalAddr()) - c.state = state_closing -} - -func (c *UTPConn) syn_sent() { - ulog.Printf(2, "Conn(%v): Change state: SYN_SENT", c.LocalAddr()) - c.state = state_syn_sent -} - -func (c *UTPConn) connected() { - ulog.Printf(2, "Conn(%v): Change state: CONNECTED", c.LocalAddr()) - c.state = state_connected -} - -func (c *UTPConn) fin_sent() { - ulog.Printf(2, "Conn(%v): Change state: FIN_SENT", c.LocalAddr()) - c.state = state_fin_sent -} - -type state struct { - data func(c *UTPConn, p *packet) - fin func(c *UTPConn, p *packet) - state func(c *UTPConn, p *packet) - exit func(c *UTPConn) - active bool - closed bool -} - -var state_closed state = state{ - closed: true, -} - -var state_closing state = state{ - data: func(c *UTPConn, p *packet) { - select { - case c.readch <- append([]byte(nil), p.payload...): - case <-c.readchch: - } - if c.recvbuf.empty() && c.sendbuf.empty() { - c.close() - } - }, - state: func(c *UTPConn, p *packet) { - if c.recvbuf.empty() && c.sendbuf.empty() { - c.close() - } - }, -} - -var state_syn_sent state = state{ - state: func(c *UTPConn, p *packet) { - c.recvbuf = newPacketBuffer(window_size, int(p.header.seq)) - c.connected() - c.connch <- nil - }, - exit: func(c *UTPConn) { - go func() { - select { - case c.outch <- &outgoingPacket{st_fin, nil, nil}: - case <-c.outchch: - } - }() - c.fin_sent() - }, - active: true, -} - -var state_connected state = state{ - data: func(c *UTPConn, p *packet) { - select { - case c.readch <- append([]byte(nil), p.payload...): - case <-c.readchch: - } - }, - fin: func(c *UTPConn, p *packet) { - if c.recvbuf.empty() && c.sendbuf.empty() { - c.close() - } else { - c.closing() - } - }, - exit: func(c *UTPConn) { - go func() { - select { - case c.outch <- &outgoingPacket{st_fin, nil, nil}: - case <-c.outchch: - } - }() - c.fin_sent() - }, - active: true, -} - -var state_fin_sent state = state{ - state: func(c *UTPConn, p *packet) { - if p.header.ack == c.eofid { - if c.recvbuf.empty() && c.sendbuf.empty() { - c.close() - } else { - c.closing() - } - } - }, -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go new file mode 100644 index 0000000..8645d32 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go @@ -0,0 +1,87 @@ +package utp + +import ( + "bytes" + "testing" +) + +func TestReadWrite(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + payload := []byte("abcdefgh") + + ch := make(chan int) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + var buf [256]byte + length, err := c.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(payload, buf[:length]) { + t.Errorf("expected payload of %v; got %v", payload, buf[:length]) + } + + ch <- 0 + }() + + c, err := DialUTP("utp", nil, l.Addr().(*Addr)) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + _, err = c.Write(payload) + if err != nil { + t.Fatal(err) + } + + <-ch +} + +func TestClose(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + c.Close() + }() + + c, err := DialUTP("utp", nil, l.Addr().(*Addr)) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + var b [128]byte + _, err = c.Read(b[:]) + if err == nil { + t.Fatal("Read should fail") + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/dial.go b/Godeps/_workspace/src/github.com/h2so5/utp/dial.go index 10e76fa..ae1bf4f 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/dial.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/dial.go @@ -2,24 +2,57 @@ package utp import ( "errors" + "math" + "math/rand" "net" "time" ) -func Dial(n, addr string) (*UTPConn, error) { - raddr, err := ResolveUTPAddr(n, addr) +// DialUTP connects to the remote address raddr on the network net, +// which must be "utp", "utp4", or "utp6". If laddr is not nil, it is +// used as the local address for the connection. +func DialUTP(n string, laddr, raddr *Addr) (*Conn, error) { + return DialUTPTimeout(n, laddr, raddr, 0) +} + +// DialUTPTimeout acts like Dial but takes a timeout. +// The timeout includes name resolution, if required. +func DialUTPTimeout(n string, laddr, raddr *Addr, timeout time.Duration) (*Conn, error) { + conn, err := getSharedBaseConn(n, laddr) if err != nil { return nil, err } - return DialUTP(n, nil, raddr) -} -func DialUTP(n string, laddr, raddr *UTPAddr) (*UTPConn, error) { - return dial(n, laddr, raddr, 0) -} + id := uint16(rand.Intn(math.MaxUint16)) + c := newConn() + c.conn = conn + c.raddr = raddr.Addr + c.rid = id + c.sid = id + 1 + c.seq = 1 + c.state = stateSynSent + c.sendbuf = newPacketBuffer(windowSize*2, 1) + c.conn.Register(int32(c.rid), c.recv) + go c.loop() + c.synch <- 0 -func DialUTPTimeout(n string, laddr, raddr *UTPAddr, timeout time.Duration) (*UTPConn, error) { - return dial(n, laddr, raddr, timeout) + var t <-chan time.Time + if timeout != 0 { + t = time.After(timeout) + } + + select { + case <-c.connch: + case <-t: + c.Close() + return nil, &net.OpError{ + Op: "dial", + Net: c.LocalAddr().Network(), + Addr: c.LocalAddr(), + Err: errTimeout, + } + } + return c, nil } // A Dialer contains options for connecting to an address. @@ -49,18 +82,18 @@ type Dialer struct { // Dial connects to the address on the named network. // // See func Dial for a description of the network and address parameters. -func (d *Dialer) Dial(n, addr string) (*UTPConn, error) { - raddr, err := ResolveUTPAddr(n, addr) +func (d *Dialer) Dial(n, addr string) (*Conn, error) { + raddr, err := ResolveAddr(n, addr) if err != nil { return nil, err } - var laddr *UTPAddr + var laddr *Addr if d.LocalAddr != nil { var ok bool - laddr, ok = d.LocalAddr.(*UTPAddr) + laddr, ok = d.LocalAddr.(*Addr) if !ok { - return nil, errors.New("Dialer.LocalAddr is not a UTPAddr") + return nil, errors.New("Dialer.LocalAddr is not a Addr") } } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go new file mode 100644 index 0000000..7846076 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go @@ -0,0 +1,52 @@ +package utp + +import ( + "testing" + "time" +) + +func TestDial(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + ch := make(chan struct{}) + go func() { + l.Accept() + close(ch) + }() + + c, err := DialUTP("utp", nil, l.Addr().(*Addr)) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + <-ch +} + +func TestDialFastTimeout(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + defer l.Close() + _, err = (&Dialer{ + Timeout: time.Nanosecond, + }).Dial("utp", l.Addr().String()) + if err == nil { + t.Fatal("expected an error") + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/listener.go b/Godeps/_workspace/src/github.com/h2so5/utp/listener.go index 25eb0ef..cdf9a7c 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/listener.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/listener.go @@ -1,329 +1,146 @@ package utp import ( - "errors" "math" "math/rand" "net" + "sync" + "sync/atomic" "syscall" "time" ) -type UTPListener struct { +// Listener is a UTP network listener. Clients should typically +// use variables of type Listener instead of assuming UTP. +type Listener struct { // RawConn represents an out-of-band connection. // This allows a single socket to handle multiple protocols. RawConn net.PacketConn - conn net.PacketConn - conns map[uint16]*UTPConn - accept chan (*UTPConn) - err chan (error) - lasterr error - deadline time.Time - closech chan int - connch chan uint16 - closed bool + conn *baseConn + deadline time.Time + deadlineMutex sync.RWMutex + closed int32 } -func Listen(n, laddr string) (*UTPListener, error) { - addr, err := ResolveUTPAddr(n, laddr) - if err != nil { - return nil, err - } - return ListenUTP(n, addr) -} +func (l *Listener) ok() bool { return l != nil && l.conn != nil } -func ListenUTP(n string, laddr *UTPAddr) (*UTPListener, error) { - udpnet, err := utp2udp(n) +// Listen announces on the UTP address laddr and returns a UTP +// listener. Net must be "utp", "utp4", or "utp6". If laddr has a +// port of 0, ListenUTP will choose an available port. The caller can +// use the Addr method of Listener to retrieve the chosen address. +func Listen(n string, laddr *Addr) (*Listener, error) { + conn, err := newBaseConn(n, laddr) if err != nil { return nil, err } - conn, err := listenPacket(udpnet, laddr.Addr.String()) - if err != nil { - return nil, err - } - - l := UTPListener{ - RawConn: newRawConn(conn), + l := &Listener{ + RawConn: conn, conn: conn, - conns: make(map[uint16]*UTPConn), - accept: make(chan (*UTPConn), 10), - err: make(chan (error), 1), - closech: make(chan int), - connch: make(chan uint16), - lasterr: nil, } - - l.listen() - return &l, nil + conn.Register(-1, nil) + return l, nil } -type incoming struct { - p *packet - addr net.Addr -} - -func (l *UTPListener) listen() { - inch := make(chan incoming) - raw := l.RawConn.(*rawConn) - - // reads udp packets - go func() { - for { - var buf [mtu]byte - len, addr, err := l.conn.ReadFrom(buf[:]) - if err != nil { - l.err <- err - return - } - p, err := readPacket(buf[:len]) - if err == nil { - inch <- incoming{p, addr} - } else { - select { - case <-raw.closed: - default: - i := rawIncoming{b: buf[:len], addr: addr} - select { - case raw.in <- i: - default: - // discard the oldest packet - <-raw.in - raw.in <- i - } - } - } - } - }() - - go func() { - for { - select { - case i := <-inch: - l.processPacket(i.p, i.addr) - case <-l.closech: - ulog.Printf(2, "Listener(%v): Stop listening", l.conn.LocalAddr()) - close(l.accept) - l.closed = true - case id := <-l.connch: - if _, ok := l.conns[id]; !ok { - delete(l.conns, id+1) - ulog.Printf(2, "Listener(%v): Connection closed #%d (alive: %d)", l.conn.LocalAddr(), id, len(l.conns)) - if l.closed && len(l.conns) == 0 { - ulog.Printf(2, "Listener(%v): All accepted connections are closed", l.conn.LocalAddr()) - l.conn.Close() - ulog.Printf(1, "Listener(%v): Closed", l.conn.LocalAddr()) - return - } - } - } - } - }() - - ulog.Printf(1, "Listener(%v): Start listening", l.conn.LocalAddr()) -} - -func listenPacket(n, addr string) (net.PacketConn, error) { - if n == "mem" { - return nil, errors.New("TODO implement in-memory packet connection") - } - return net.ListenPacket(n, addr) -} - -func (l *UTPListener) processPacket(p *packet, addr net.Addr) { - switch p.header.typ { - case st_data, st_fin, st_state, st_reset: - if c, ok := l.conns[p.header.id]; ok { - select { - case c.recvch <- p: - case <-c.recvchch: - } - } - case st_syn: - if l.closed { - return - } - sid := p.header.id + 1 - if _, ok := l.conns[p.header.id]; !ok { - seq := rand.Intn(math.MaxUint16) - - c := newUTPConn() - c.conn = l.conn - c.raddr = addr - c.rid = p.header.id + 1 - c.sid = p.header.id - c.seq = uint16(seq) - c.ack = p.header.seq - c.diff = currentMicrosecond() - p.header.t - c.state = state_connected - c.closech = l.connch - c.recvbuf = newPacketBuffer(window_size, int(p.header.seq)) - c.sendbuf = newPacketBuffer(window_size, seq) - - go c.loop() - select { - case c.recvch <- p: - case <-c.recvchch: - } - - l.conns[sid] = c - ulog.Printf(2, "Listener(%v): New incoming connection #%d from %v (alive: %d)", l.conn.LocalAddr(), sid, addr, len(l.conns)) - - l.accept <- c - } - } -} - -func (l *UTPListener) Accept() (net.Conn, error) { +// Accept implements the Accept method in the Listener interface; it +// waits for the next call and returns a generic Conn. +func (l *Listener) Accept() (net.Conn, error) { return l.AcceptUTP() } -func (l *UTPListener) AcceptUTP() (*UTPConn, error) { - if l == nil || l.conn == nil { +// AcceptUTP accepts the next incoming call and returns the new +// connection. +func (l *Listener) AcceptUTP() (*Conn, error) { + if !l.ok() { return nil, syscall.EINVAL } - if l.lasterr != nil { - return nil, l.lasterr - } - var timeout <-chan time.Time - if !l.deadline.IsZero() { - timeout = time.After(l.deadline.Sub(time.Now())) - } - select { - case conn := <-l.accept: - if conn == nil { - return nil, errors.New("use of closed network connection") + if !l.isOpen() { + return nil, &net.OpError{ + Op: "accept", + Net: l.conn.LocalAddr().Network(), + Addr: l.conn.LocalAddr(), + Err: errClosing, } - return conn, nil - case err := <-l.err: - l.lasterr = err - return nil, err - case <-timeout: - return nil, &timeoutError{} } + l.deadlineMutex.RLock() + d := timeToDeadline(l.deadline) + l.deadlineMutex.RUnlock() + p, err := l.conn.RecvSyn(d) + if err != nil { + return nil, &net.OpError{ + Op: "accept", + Net: l.conn.LocalAddr().Network(), + Addr: l.conn.LocalAddr(), + Err: errClosing, + } + } + + seq := rand.Intn(math.MaxUint16) + rid := p.header.id + 1 + + c := newConn() + c.state = stateConnected + c.conn = l.conn + c.raddr = p.addr + c.rid = p.header.id + 1 + c.sid = p.header.id + c.seq = uint16(seq) + c.ack = p.header.seq + c.recvbuf = newPacketBuffer(windowSize, int(p.header.seq)) + c.sendbuf = newPacketBuffer(windowSize*2, seq) + l.conn.Register(int32(rid), c.recv) + go c.loop() + c.recv <- p + + ulog.Printf(2, "baseConn(%v): accept #%d from %v", c.LocalAddr(), c.rid, c.raddr) + return c, nil } -func (l *UTPListener) Addr() net.Addr { - return &UTPAddr{Addr: l.conn.LocalAddr()} +// Addr returns the listener's network address, a *Addr. +func (l *Listener) Addr() net.Addr { + if !l.ok() { + return nil + } + return l.conn.LocalAddr() } -func (l *UTPListener) Close() error { - if l == nil || l.conn == nil { +// Close stops listening on the UTP address. +// Already Accepted connections are not closed. +func (l *Listener) Close() error { + if !l.ok() { return syscall.EINVAL } - l.closech <- 0 - l.RawConn.Close() + if !l.close() { + return &net.OpError{ + Op: "close", + Net: l.conn.LocalAddr().Network(), + Addr: l.conn.LocalAddr(), + Err: errClosing, + } + } return nil } -func (l *UTPListener) SetDeadline(t time.Time) error { - if l == nil || l.conn == nil { +// SetDeadline sets the deadline associated with the listener. +// A zero time value disables the deadline. +func (l *Listener) SetDeadline(t time.Time) error { + if !l.ok() { return syscall.EINVAL } + l.deadlineMutex.Lock() + defer l.deadlineMutex.Unlock() l.deadline = t return nil } -type rawIncoming struct { - b []byte - addr net.Addr +func (l *Listener) close() bool { + if atomic.CompareAndSwapInt32(&l.closed, 0, 1) { + l.conn.Unregister(-1) + return true + } + return false } -type rawConn struct { - conn net.PacketConn - rdeadline, wdeadline time.Time - in chan rawIncoming - closed chan int -} - -func newRawConn(conn net.PacketConn) *rawConn { - return &rawConn{ - conn: conn, - in: make(chan rawIncoming, 100), - closed: make(chan int), - } -} - -func (c *rawConn) ok() bool { return c != nil && c.conn != nil } - -func (c *rawConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { - if !c.ok() { - return 0, nil, syscall.EINVAL - } - select { - case <-c.closed: - return 0, nil, errors.New("use of closed network connection") - default: - } - var timeout <-chan time.Time - if !c.rdeadline.IsZero() { - timeout = time.After(c.rdeadline.Sub(time.Now())) - } - select { - case r := <-c.in: - return copy(b, r.b), r.addr, nil - case <-timeout: - return 0, nil, &timeoutError{} - } -} - -func (c *rawConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - select { - case <-c.closed: - return 0, errors.New("use of closed network connection") - default: - } - return c.conn.WriteTo(b, addr) -} - -func (c *rawConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - select { - case <-c.closed: - return errors.New("use of closed network connection") - default: - close(c.closed) - } - return nil -} - -func (c *rawConn) LocalAddr() net.Addr { - if !c.ok() { - return nil - } - return c.conn.LocalAddr() -} - -func (c *rawConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - if err := c.SetReadDeadline(t); err != nil { - return err - } - if err := c.SetWriteDeadline(t); err != nil { - return err - } - return nil -} - -func (c *rawConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.rdeadline = t - return nil -} - -func (c *rawConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.wdeadline = t - return nil +func (l *Listener) isOpen() bool { + return atomic.LoadInt32(&l.closed) == 0 } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go new file mode 100644 index 0000000..e9018a3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go @@ -0,0 +1,68 @@ +package utp + +import ( + "net" + "testing" +) + +func TestListenerAccept(t *testing.T) { + addr, err := ResolveAddr("utp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + defer l.Close() + + uaddr, err := net.ResolveUDPAddr("udp", l.Addr().String()) + if err != nil { + t.Fatal(err) + } + + uc, err := net.DialUDP("udp", nil, uaddr) + if err != nil { + t.Fatal(err) + } + defer uc.Close() + + for i := 0; i < 1; i++ { + p := &packet{header: header{typ: stSyn, ver: version, id: uint16(i)}} + payload, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + go func() { + uc.Write(payload) + }() + + a, err := l.Accept() + if err != nil { + t.Fatal(err) + } + a.Close() + } +} + +func TestListenerClose(t *testing.T) { + addr, err := ResolveAddr("utp", ":0") + if err != nil { + t.Fatal(err) + } + + l, err := Listen("utp", addr) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 5; i++ { + l.Close() + } + + _, err = l.Accept() + if err == nil { + t.Fatal("Accept should fail") + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/packet.go b/Godeps/_workspace/src/github.com/h2so5/utp/packet.go index b23cac1..da3c302 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/packet.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/packet.go @@ -5,7 +5,8 @@ import ( "encoding/binary" "fmt" "io" - "sync" + "io/ioutil" + "net" ) type header struct { @@ -24,16 +25,11 @@ type packet struct { header header ext []extension payload []byte -} - -type outgoingPacket struct { - typ int - ext []extension - payload []byte + addr net.Addr } func (p *packet) MarshalBinary() ([]byte, error) { - firstExt := ext_none + firstExt := extNone if len(p.ext) > 0 { firstExt = p.ext[0].typ } @@ -68,7 +64,7 @@ func (p *packet) MarshalBinary() ([]byte, error) { if len(p.ext) > 0 { for i, e := range p.ext { - next := ext_none + next := extNone if i < len(p.ext)-1 { next = p.ext[i+1].typ } @@ -123,7 +119,7 @@ func (p *packet) UnmarshalBinary(data []byte) error { } } - for e != ext_none { + for e != extNone { currentExt := int(e) var l uint8 var ext = []interface{}{ @@ -173,68 +169,30 @@ func (p *packet) UnmarshalBinary(data []byte) error { p.header.typ = int((tv >> 4) & 0xF) p.header.ver = int(tv & 0xF) - l := buf.Len() - if l > 0 { - p.payload = p.payload[:l] - _, err := buf.Read(p.payload[:]) - if err != nil { - return err - } + data, err := ioutil.ReadAll(buf) + if err != nil { + return err } + p.payload = data return nil } func (p packet) String() string { - var s string = fmt.Sprintf("[%d ", p.header.id) + s := fmt.Sprintf("[%d ", p.header.id) switch p.header.typ { - case st_data: + case stData: s += "ST_DATA" - case st_fin: + case stFin: s += "ST_FIN" - case st_state: + case stState: s += "ST_STATE" - case st_reset: + case stReset: s += "ST_RESET" - case st_syn: + case stSyn: s += "ST_SYN" } s += fmt.Sprintf(" seq:%d ack:%d len:%d", p.header.seq, p.header.ack, len(p.payload)) s += "]" return s } - -var globalPool packetPool - -type packetPool struct { - root *packetPoolNode - mutex sync.Mutex -} - -type packetPoolNode struct { - p *packet - next *packetPoolNode -} - -func (o *packetPool) get() *packet { - o.mutex.Lock() - defer o.mutex.Unlock() - r := o.root - if r != nil { - o.root = o.root.next - return r.p - } else { - return &packet{ - payload: make([]byte, 0, mss), - } - } -} - -func (o *packetPool) put(p *packet) { - o.mutex.Lock() - defer o.mutex.Unlock() - o.root = &packetPoolNode{ - p: p, - next: o.root, - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go new file mode 100644 index 0000000..994fa2c --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go @@ -0,0 +1,64 @@ +package utp + +import ( + "io" + "reflect" + "testing" +) + +func TestPacketBinary(t *testing.T) { + h := header{ + typ: stFin, + ver: version, + id: 100, + t: 50000, + diff: 10000, + wnd: 65535, + seq: 100, + ack: 200, + } + + e := []extension{ + extension{ + typ: extSelectiveAck, + payload: []byte{0, 1, 0, 1}, + }, + extension{ + typ: extSelectiveAck, + payload: []byte{100, 0, 200, 0}, + }, + } + + p := packet{ + header: h, + ext: e, + payload: []byte("abcdefg"), + } + + b, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + p2 := packet{payload: make([]byte, 0, mss)} + err = p2.UnmarshalBinary(b) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(p, p2) { + t.Errorf("expected packet of %v; got %v", p, p2) + } +} + +func TestUnmarshalShortPacket(t *testing.T) { + b := make([]byte, 18) + p := packet{} + err := p.UnmarshalBinary(b) + + if err == nil { + t.Fatal("UnmarshalBinary should fail") + } else if err != io.EOF { + t.Fatal(err) + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go index 0a6ebf5..a377dad 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go @@ -111,7 +111,11 @@ func log(format string, vals ...interface{}) { // Listen listens and accepts one incoming uTP connection on a given port, // and pipes all incoming data to os.Stdout. func Listen(localAddr string) error { - l, err := utp.Listen("utp", localAddr) + laddr, err := utp.ResolveAddr("utp", localAddr) + if err != nil { + return fmt.Errorf("failed to resolve address %s", localAddr) + } + l, err := utp.Listen("utp", laddr) if err != nil { return err } @@ -138,7 +142,7 @@ func Dial(localAddr, remoteAddr string) error { var laddr net.Addr var err error if localAddr != "" { - laddr, err = utp.ResolveUTPAddr("utp", localAddr) + laddr, err = utp.ResolveAddr("utp", localAddr) if err != nil { return fmt.Errorf("failed to resolve address %s", localAddr) } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/utp.go b/Godeps/_workspace/src/github.com/h2so5/utp/utp.go index 5a92bae..1ac4b31 100644 --- a/Godeps/_workspace/src/github.com/h2so5/utp/utp.go +++ b/Godeps/_workspace/src/github.com/h2so5/utp/utp.go @@ -1,25 +1,38 @@ package utp -import "time" +import ( + "errors" + "time" +) const ( version = 1 - st_data = 0 - st_fin = 1 - st_state = 2 - st_reset = 3 - st_syn = 4 + stData = 0 + stFin = 1 + stState = 2 + stReset = 3 + stSyn = 4 - ext_none = 0 - ext_selective_ack = 1 + stateClosed = iota + stateClosing + stateSynSent + stateConnected + stateFinSent - header_size = 20 - mtu = 3200 - mss = mtu - header_size - window_size = 100 + extNone = 0 + extSelectiveAck = 1 - reset_timeout = time.Second + headerSize = 20 + mtu = 3200 + mss = mtu - headerSize + windowSize = 100 + packetBufferSize = 256 + readBufferSize = 1048576 + maxRetry = 3 + + maxUdpPayload = 65507 + resetTimeout = time.Second ) type timeoutError struct{} @@ -27,3 +40,8 @@ type timeoutError struct{} func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } + +var ( + errTimeout error = &timeoutError{} + errClosing = errors.New("use of closed network connection") +) diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go deleted file mode 100644 index 7e4f11a..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go +++ /dev/null @@ -1,601 +0,0 @@ -package utp - -import ( - "bytes" - "io" - "io/ioutil" - "math" - "math/rand" - "net" - "reflect" - "testing" - "time" -) - -func init() { - rand.Seed(time.Now().Unix()) -} - -func TestReadWrite(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - raddr, err := ResolveUTPAddr("utp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - - c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond)) - if err != nil { - t.Fatal(err) - } - - s, err := ln.Accept() - if err != nil { - t.Fatal(err) - } - ln.Close() - - payload := []byte("Hello!") - _, err = c.Write(payload) - if err != nil { - t.Fatal(err) - } - - err = s.SetDeadline(time.Now().Add(1000 * time.Millisecond)) - if err != nil { - t.Fatal(err) - } - - var buf [256]byte - l, err := s.Read(buf[:]) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(payload, buf[:l]) { - t.Errorf("expected payload of %v; got %v", payload, buf[:l]) - } - - payload2 := []byte("World!") - _, err = s.Write(payload2) - if err != nil { - t.Fatal(err) - } - - err = c.SetDeadline(time.Now().Add(1000 * time.Millisecond)) - if err != nil { - t.Fatal(err) - } - - l, err = c.Read(buf[:]) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(payload2, buf[:l]) { - t.Errorf("expected payload of %v; got %v", payload2, buf[:l]) - } -} - -func TestRawReadWrite(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - raddr, err := net.ResolveUDPAddr("udp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - - c, err := net.DialUDP("udp", nil, raddr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - payload := []byte("Hello!") - _, err = c.Write(payload) - if err != nil { - t.Fatal(err) - } - - var buf [256]byte - n, addr, err := ln.RawConn.ReadFrom(buf[:]) - if !bytes.Equal(payload, buf[:n]) { - t.Errorf("expected payload of %v; got %v", payload, buf[:n]) - } - if addr.String() != c.LocalAddr().String() { - t.Errorf("expected addr of %v; got %v", c.LocalAddr(), addr.String()) - } -} - -func TestLongReadWriteC2S(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - raddr, err := ResolveUTPAddr("utp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - - c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond)) - if err != nil { - t.Fatal(err) - } - - s, err := ln.Accept() - if err != nil { - t.Fatal(err) - } - defer s.Close() - ln.Close() - - var payload [10485760]byte - for i := range payload { - payload[i] = byte(rand.Int()) - } - - rch := make(chan []byte) - ech := make(chan error, 2) - - go func() { - defer c.Close() - _, err := c.Write(payload[:]) - if err != nil { - ech <- err - } - }() - - go func() { - b, err := ioutil.ReadAll(s) - if err != nil { - ech <- err - rch <- nil - } else { - ech <- nil - rch <- b - } - }() - - err = <-ech - if err != nil { - t.Fatal(err) - } - - r := <-rch - if r == nil { - return - } - - if !bytes.Equal(r, payload[:]) { - t.Errorf("expected payload of %d; got %d", len(payload[:]), len(r)) - } -} - -func TestLongReadWriteS2C(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - raddr, err := ResolveUTPAddr("utp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - - c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond)) - if err != nil { - t.Fatal(err) - } - - s, err := ln.Accept() - if err != nil { - t.Fatal(err) - } - defer s.Close() - ln.Close() - - var payload [10485760]byte - for i := range payload { - payload[i] = byte(rand.Int()) - } - - rch := make(chan []byte) - ech := make(chan error, 2) - - go func() { - defer s.Close() - _, err := s.Write(payload[:]) - if err != nil { - ech <- err - } - }() - - go func() { - b, err := ioutil.ReadAll(c) - if err != nil { - ech <- err - rch <- nil - } else { - ech <- nil - rch <- b - } - }() - - err = <-ech - if err != nil { - t.Fatal(err) - } - - r := <-rch - if r == nil { - return - } - - if !bytes.Equal(r, payload[:]) { - t.Errorf("expected payload of %d; got %d", len(payload[:]), len(r)) - } -} - -func TestAccept(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - err = ln.SetDeadline(time.Now().Add(100 * time.Millisecond)) - _, err = ln.Accept() - if err != nil { - t.Fatal(err) - } -} - -func TestAcceptDeadline(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - err = ln.SetDeadline(time.Now().Add(time.Millisecond)) - _, err = ln.Accept() - if err == nil { - t.Fatal("Accept should failed") - } -} - -func TestAcceptClosedListener(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - err = ln.Close() - if err != nil { - t.Fatal(err) - } - _, err = ln.Accept() - if err == nil { - t.Fatal("Accept should failed") - } - _, err = ln.Accept() - if err == nil { - t.Fatal("Accept should failed") - } -} - -func TestDialer(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - d := Dialer{} - c, err := d.Dial("utp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - defer c.Close() -} - -func TestDialerAddrs(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - laddr, err := ResolveUTPAddr("utp", "127.0.0.1:45678") - if err != nil { - t.Fatal(err) - } - - d := Dialer{LocalAddr: laddr} - c1, err := d.Dial("utp", ln.Addr().String()) - if err != nil { - t.Fatal(err) - } - defer c1.Close() - - c2, err := ln.Accept() - if err != nil { - t.Fatal(err) - } - defer c2.Close() - - eq := func(a, b net.Addr) bool { - return a.String() == b.String() - } - - if !eq(d.LocalAddr, c2.RemoteAddr()) { - t.Fatal("dialer.LocalAddr not equal to c2.RemoteAddr ") - } - if !eq(c1.LocalAddr(), c2.RemoteAddr()) { - t.Fatal("c1.LocalAddr not equal to c2.RemoteAddr ") - } - if !eq(c2.LocalAddr(), c1.RemoteAddr()) { - t.Fatal("c2.LocalAddr not equal to c1.RemoteAddr ") - } -} - -func TestDialerTimeout(t *testing.T) { - timeout := time.Millisecond * 200 - d := Dialer{Timeout: timeout} - done := make(chan struct{}) - - go func() { - _, err := d.Dial("utp", "127.0.0.1:34567") - if err == nil { - t.Fatal("should not connect") - } - done <- struct{}{} - }() - - select { - case <-time.After(timeout * 2): - t.Fatal("should have ended already") - case <-done: - } -} - -func TestPacketBinary(t *testing.T) { - h := header{ - typ: st_fin, - ver: version, - id: 100, - t: 50000, - diff: 10000, - wnd: 65535, - seq: 100, - ack: 200, - } - - e := []extension{ - extension{ - typ: ext_selective_ack, - payload: []byte{0, 1, 0, 1}, - }, - extension{ - typ: ext_selective_ack, - payload: []byte{100, 0, 200, 0}, - }, - } - - p := packet{ - header: h, - ext: e, - payload: []byte("abcdefg"), - } - - b, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - - p2 := packet{payload: make([]byte, 0, mss)} - err = p2.UnmarshalBinary(b) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(p, p2) { - t.Errorf("expected packet of %v; got %v", p, p2) - } -} - -func TestUnmarshalShortPacket(t *testing.T) { - b := make([]byte, 18) - p := packet{} - err := p.UnmarshalBinary(b) - - if err == nil { - t.Fatal("UnmarshalBinary should fail") - } else if err != io.EOF { - t.Fatal(err) - } -} - -func TestWriteOnClosedChannel(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond) - if err != nil { - t.Fatal(err) - } - - go func() { - for { - _, err := c.Write([]byte{100}) - if err != nil { - return - } - } - }() - - c.Close() -} - -func TestReadOnClosedChannel(t *testing.T) { - ln, err := Listen("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond) - if err != nil { - t.Fatal(err) - } - - go func() { - for { - var buf [16]byte - _, err := c.Read(buf[:]) - if err != nil { - return - } - } - }() - - c.Close() -} - -func TestPacketBuffer(t *testing.T) { - size := 12 - b := newPacketBuffer(12, 1) - - if b.space() != size { - t.Errorf("expected space == %d; got %d", size, b.space()) - } - - for i := 1; i <= size; i++ { - b.push(&packet{header: header{seq: uint16(i)}}) - } - - if b.space() != 0 { - t.Errorf("expected space == 0; got %d", b.space()) - } - - a := []byte{255, 7} - ack := b.generateSelectiveACK() - if !bytes.Equal(a, ack) { - t.Errorf("expected ack == %v; got %v", a, ack) - } - - err := b.push(&packet{header: header{seq: 15}}) - if err == nil { - t.Fatal("push should fail") - } - - all := b.all() - if len(all) != size { - t.Errorf("expected %d packets sequence; got %d", size, len(all)) - } - - f := b.fetch(6) - if f == nil { - t.Fatal("fetch should not fail") - } - - b.compact() - - err = b.push(&packet{header: header{seq: 15}}) - if err != nil { - t.Fatal(err) - } - - err = b.push(&packet{header: header{seq: 17}}) - if err != nil { - t.Fatal(err) - } - - for i := 7; i <= size; i++ { - f := b.fetch(uint16(i)) - if f == nil { - t.Fatal("fetch should not fail") - } - } - - a = []byte{128, 2} - ack = b.generateSelectiveACK() - if !bytes.Equal(a, ack) { - t.Errorf("expected ack == %v; got %v", a, ack) - } - - all = b.all() - if len(all) != 2 { - t.Errorf("expected 2 packets sequence; got %d", len(all)) - } - - b.compact() - if b.space() != 9 { - t.Errorf("expected space == 9; got %d", b.space()) - } - - ack = b.generateSelectiveACK() - b.processSelectiveACK(ack) - - all = b.all() - if len(all) != 1 { - t.Errorf("expected size == 1; got %d", len(all)) - } -} - -func TestPacketBufferBoundary(t *testing.T) { - begin := math.MaxUint16 - 3 - b := newPacketBuffer(12, begin) - for i := begin; i != 5; i = (i + 1) % (math.MaxUint16 + 1) { - err := b.push(&packet{header: header{seq: uint16(i)}}) - if err != nil { - t.Fatal(err) - } - } -} - -func TestTimedBufferNode(t *testing.T) { - b := timedBuffer{d: time.Millisecond * 100} - b.push(100) - b.push(200) - time.Sleep(time.Millisecond * 200) - b.push(300) - b.push(400) - m := b.min() - if m != 300 { - t.Errorf("expected min == 300; got %d", m) - } -} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-base58/LICENSE b/Godeps/_workspace/src/github.com/jbenet/go-base58/LICENSE new file mode 100644 index 0000000..0d760cb --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-base58/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2013 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/jbenet/go-base58/README.md b/Godeps/_workspace/src/github.com/jbenet/go-base58/README.md new file mode 100644 index 0000000..ece2433 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-base58/README.md @@ -0,0 +1,66 @@ +# go-base58 + +I extracted this package from https://github.com/conformal/btcutil to provide a simple base58 package that +- defaults to base58-check (btc) +- and allows using different alphabets. + +## Usage + +```go +package main + +import ( + "fmt" + b58 "github.com/jbenet/go-base58" +) + +func main() { + buf := []byte{255, 254, 253, 252} + fmt.Printf("buffer: %v\n", buf) + + str := b58.Encode(buf) + fmt.Printf("encoded: %s\n", str) + + buf2 := b58.Decode(str) + fmt.Printf("decoded: %v\n", buf2) +} +``` + +### Another alphabet + +```go +package main + +import ( + "fmt" + b58 "github.com/jbenet/go-base58" +) + +const BogusAlphabet = "ZYXWVUTSRQPNMLKJHGFEDCBAzyxwvutsrqponmkjihgfedcba987654321" + + +func encdec(alphabet string) { + fmt.Printf("using: %s\n", alphabet) + + buf := []byte{255, 254, 253, 252} + fmt.Printf("buffer: %v\n", buf) + + str := b58.EncodeAlphabet(buf, alphabet) + fmt.Printf("encoded: %s\n", str) + + buf2 := b58.DecodeAlphabet(str, alphabet) + fmt.Printf("decoded: %v\n\n", buf2) +} + + +func main() { + encdec(b58.BTCAlphabet) + encdec(b58.FlickrAlphabet) + encdec(BogusAlphabet) +} +``` + + +## License + +Package base58 (and the original btcutil) are licensed under the ISC License. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-base58/base58.go b/Godeps/_workspace/src/github.com/jbenet/go-base58/base58.go new file mode 100644 index 0000000..ad91df5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-base58/base58.go @@ -0,0 +1,90 @@ +// Copyright (c) 2013-2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. +// Modified by Juan Benet (juan@benet.ai) + +package base58 + +import ( + "math/big" + "strings" +) + +// alphabet is the modified base58 alphabet used by Bitcoin. +const BTCAlphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +const FlickrAlphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" + +var bigRadix = big.NewInt(58) +var bigZero = big.NewInt(0) + +// Decode decodes a modified base58 string to a byte slice, using BTCAlphabet +func Decode(b string) []byte { + return DecodeAlphabet(b, BTCAlphabet) +} + +// Encode encodes a byte slice to a modified base58 string, using BTCAlphabet +func Encode(b []byte) string { + return EncodeAlphabet(b, BTCAlphabet) +} + +// DecodeAlphabet decodes a modified base58 string to a byte slice, using alphabet. +func DecodeAlphabet(b, alphabet string) []byte { + answer := big.NewInt(0) + j := big.NewInt(1) + + for i := len(b) - 1; i >= 0; i-- { + tmp := strings.IndexAny(alphabet, string(b[i])) + if tmp == -1 { + return []byte("") + } + idx := big.NewInt(int64(tmp)) + tmp1 := big.NewInt(0) + tmp1.Mul(j, idx) + + answer.Add(answer, tmp1) + j.Mul(j, bigRadix) + } + + tmpval := answer.Bytes() + + var numZeros int + for numZeros = 0; numZeros < len(b); numZeros++ { + if b[numZeros] != alphabet[0] { + break + } + } + flen := numZeros + len(tmpval) + val := make([]byte, flen, flen) + copy(val[numZeros:], tmpval) + + return val +} + +// Encode encodes a byte slice to a modified base58 string, using alphabet +func EncodeAlphabet(b []byte, alphabet string) string { + x := new(big.Int) + x.SetBytes(b) + + answer := make([]byte, 0, len(b)*136/100) + for x.Cmp(bigZero) > 0 { + mod := new(big.Int) + x.DivMod(x, bigRadix, mod) + answer = append(answer, alphabet[mod.Int64()]) + } + + // leading zero bytes + for _, i := range b { + if i != 0 { + break + } + answer = append(answer, alphabet[0]) + } + + // reverse + alen := len(answer) + for i := 0; i < alen/2; i++ { + answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] + } + + return string(answer) +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-base58/base58_test.go b/Godeps/_workspace/src/github.com/jbenet/go-base58/base58_test.go new file mode 100644 index 0000000..516781b --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-base58/base58_test.go @@ -0,0 +1,96 @@ +// Copyright (c) 2013-2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +var stringTests = []struct { + in string + out string +}{ + {"", ""}, + {" ", "Z"}, + {"-", "n"}, + {"0", "q"}, + {"1", "r"}, + {"-1", "4SU"}, + {"11", "4k8"}, + {"abc", "ZiCa"}, + {"1234598760", "3mJr7AoUXx2Wqd"}, + {"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, + {"00000000000000000000000000000000000000000000000000000000000000", "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y"}, +} + +var invalidStringTests = []struct { + in string + out string +}{ + {"0", ""}, + {"O", ""}, + {"I", ""}, + {"l", ""}, + {"3mJr0", ""}, + {"O3yxU", ""}, + {"3sNI", ""}, + {"4kl8", ""}, + {"0OIl", ""}, + {"!@#$%^&*()-_=+~`", ""}, +} + +var hexTests = []struct { + in string + out string +}{ + {"61", "2g"}, + {"626262", "a3gV"}, + {"636363", "aPEr"}, + {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, + {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, + {"516b6fcd0f", "ABnLTmg"}, + {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, + {"572e4794", "3EFU7m"}, + {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, + {"10c8511e", "Rt5zm"}, + {"00000000000000000000", "1111111111"}, +} + +func TestBase58(t *testing.T) { + // Base58Encode tests + for x, test := range stringTests { + tmp := []byte(test.in) + if res := Encode(tmp); res != test.out { + t.Errorf("Base58Encode test #%d failed: got: %s want: %s", + x, res, test.out) + continue + } + } + + // Base58Decode tests + for x, test := range hexTests { + b, err := hex.DecodeString(test.in) + if err != nil { + t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) + continue + } + if res := Decode(test.out); bytes.Equal(res, b) != true { + t.Errorf("Base58Decode test #%d failed: got: %q want: %q", + x, res, test.in) + continue + } + } + + // Base58Decode with invalid input + for x, test := range invalidStringTests { + if res := Decode(test.in); string(res) != test.out { + t.Errorf("Base58Decode invalidString test #%d failed: got: %q want: %q", + x, res, test.out) + continue + } + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-base58/doc.go b/Godeps/_workspace/src/github.com/jbenet/go-base58/doc.go new file mode 100644 index 0000000..315c610 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-base58/doc.go @@ -0,0 +1,20 @@ +// Copyright (c) 2013-2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package base58 provides base58-check encoding. +The alphabet is modifyiable for + +Base58 Usage + +To decode a base58 string: + + rawData := base58.Base58Decode(encodedData) + +Similarly, to encode the same data: + + encodedData := base58.Base58Encode(rawData) + +*/ +package base58 diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml index 32ce5a7..7b571f4 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml @@ -1,10 +1,9 @@ language: go go: - - 1.2 - 1.3 - release - tip script: - - go test -v ./... + - go test -race -cpu=5 -v ./... diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go index 03502e9..c318873 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go @@ -2,10 +2,13 @@ package multiaddr import ( "encoding/binary" + "errors" "fmt" "net" "strconv" "strings" + + mh "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) func stringToBytes(s string) ([]byte, error) { @@ -31,17 +34,19 @@ func stringToBytes(s string) ([]byte, error) { b = append(b, CodeToVarint(p.Code)...) sp = sp[1:] - if p.Size > 0 { - if len(sp) < 1 { - return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) - } - a, err := addressStringToBytes(p, sp[0]) - if err != nil { - return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) - } - b = append(b, a...) - sp = sp[1:] + if p.Size == 0 { // no length. + continue } + + if len(sp) < 1 { + return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) + } + a, err := addressStringToBytes(p, sp[0]) + if err != nil { + return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) + } + b = append(b, a...) + sp = sp[1:] } return b, nil } @@ -51,7 +56,14 @@ func bytesToString(b []byte) (ret string, err error) { defer func() { if e := recover(); e != nil { ret = "" - err = e.(error) + switch e := e.(type) { + case error: + err = e + case string: + err = errors.New(e) + default: + err = fmt.Errorf("%v", e) + } } }() @@ -65,20 +77,38 @@ func bytesToString(b []byte) (ret string, err error) { if p.Code == 0 { return "", fmt.Errorf("no protocol with code %d", code) } - s = strings.Join([]string{s, "/", p.Name}, "") + s += "/" + p.Name - if p.Size > 0 { - a := addressBytesToString(p, b[:(p.Size/8)]) - if len(a) > 0 { - s = strings.Join([]string{s, "/", a}, "") - } - b = b[(p.Size / 8):] + if p.Size == 0 { + continue } + + size := sizeForAddr(p, b) + a, err := addressBytesToString(p, b[:size]) + if err != nil { + return "", err + } + if len(a) > 0 { + s += "/" + a + } + b = b[size:] } return s, nil } +func sizeForAddr(p Protocol, b []byte) int { + switch { + case p.Size > 0: + return (p.Size / 8) + case p.Size == 0: + return 0 + default: + size, n := ReadVarintCode(b) + return size + n + } +} + func bytesSplit(b []byte) (ret [][]byte, err error) { // panic handler, in case we try accessing bytes incorrectly. defer func() { @@ -96,7 +126,8 @@ func bytesSplit(b []byte) (ret [][]byte, err error) { return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0]) } - length := n + (p.Size / 8) + size := sizeForAddr(p, b[n:]) + length := n + size ret = append(ret, b[:length]) b = b[length:] } @@ -133,23 +164,46 @@ func addressStringToBytes(p Protocol, s string) ([]byte, error) { b := make([]byte, 2) binary.BigEndian.PutUint16(b, uint16(i)) return b, nil + + case P_IPFS: // ipfs + // the address is a varint prefixed multihash string representation + m, err := mh.FromB58String(s) + if err != nil { + return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err) + } + size := CodeToVarint(len(m)) + b := append(size, m...) + return b, nil } return []byte{}, fmt.Errorf("failed to parse %s addr: unknown", p.Name) } -func addressBytesToString(p Protocol, b []byte) string { +func addressBytesToString(p Protocol, b []byte) (string, error) { switch p.Code { // ipv4,6 case P_IP4, P_IP6: - return net.IP(b).String() + return net.IP(b).String(), nil // tcp udp dccp sctp case P_TCP, P_UDP, P_DCCP, P_SCTP: i := binary.BigEndian.Uint16(b) - return strconv.Itoa(int(i)) + return strconv.Itoa(int(i)), nil + + case P_IPFS: // ipfs + // the address is a varint-prefixed multihash string representation + size, n := ReadVarintCode(b) + b = b[n:] + if len(b) != size { + panic("inconsistent lengths") + } + m, err := mh.Cast(b) + if err != nil { + return "", err + } + return m.B58String(), nil } - return "" + return "", fmt.Errorf("unknown protocol") } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go index 373c2c1..1cb7ad4 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go @@ -64,6 +64,7 @@ func (m *multiaddr) Protocols() []Protocol { } }() + size := 0 ps := []Protocol{} b := m.bytes[:] for len(b) > 0 { @@ -75,7 +76,10 @@ func (m *multiaddr) Protocols() []Protocol { panic(fmt.Errorf("no protocol with code %d", b[0])) } ps = append(ps, p) - b = b[n+(p.Size/8):] + b = b[n:] + + size = sizeForAddr(p, b) + b = b[size:] } return ps } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go index 6545897..f0e9c36 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go @@ -32,11 +32,13 @@ func TestConstructFails(t *testing.T) { "/ip4/127.0.0.1/udp", "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/tcp", + "/ip4/127.0.0.1/ipfs", + "/ip4/127.0.0.1/ipfs/tcp", } for _, a := range cases { if _, err := NewMultiaddr(a); err == nil { - t.Errorf("should have failed: %s", a) + t.Errorf("should have failed: %s - %s", a, err) } } } @@ -55,18 +57,24 @@ func TestConstructSucceeds(t *testing.T) { "/sctp/1234", "/udp/65535", "/tcp/65535", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/udp/1234/sctp/1234", "/udp/1234/udt", "/udp/1234/utp", + "/tcp/1234/http", + "/tcp/1234/https", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/ip4/127.0.0.1/udp/1234", "/ip4/127.0.0.1/udp/0", "/ip4/127.0.0.1/tcp/1234", "/ip4/127.0.0.1/tcp/1234/", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", } for _, a := range cases { if _, err := NewMultiaddr(a); err != nil { - t.Errorf("should have succeeded: %s", a) + t.Errorf("should have succeeded: %s -- %s", a, err) } } } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv index a8b1e3a..213e9b5 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv @@ -7,5 +7,6 @@ code size name 132 16 sctp 301 0 udt 302 0 utp +421 V ipfs 480 0 http 443 0 https diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go index eaddc61..c4ee5df 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go @@ -9,7 +9,7 @@ import ( // Protocol is a Multiaddr protocol description structure. type Protocol struct { Code int - Size int + Size int // a size of -1 indicates a length-prefixed variable size Name string VCode []byte } @@ -19,14 +19,22 @@ type Protocol struct { // 2. ensuring errors in the csv don't screw up code. // 3. changing a number has to happen in two places. const ( - P_IP4 = 4 - P_TCP = 6 - P_UDP = 17 - P_DCCP = 33 - P_IP6 = 41 - P_SCTP = 132 - P_UTP = 301 - P_UDT = 302 + P_IP4 = 4 + P_TCP = 6 + P_UDP = 17 + P_DCCP = 33 + P_IP6 = 41 + P_SCTP = 132 + P_UTP = 301 + P_UDT = 302 + P_IPFS = 421 + P_HTTP = 480 + P_HTTPS = 443 +) + +// These are special sizes +const ( + LengthPrefixedVarSize = -1 ) // Protocols is the list of multiaddr protocols supported by this module. @@ -40,8 +48,9 @@ var Protocols = []Protocol{ Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP)}, Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP)}, Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT)}, - // {480, 0, "http"}, - // {443, 0, "https"}, + Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP)}, + Protocol{P_HTTPS, 0, "https", CodeToVarint(P_HTTPS)}, + Protocol{P_IPFS, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_IPFS)}, } // ProtocolWithName returns the Protocol description with given string name. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/.travis.yml b/Godeps/_workspace/src/github.com/jbenet/go-multihash/.travis.yml new file mode 100644 index 0000000..7725b30 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.3 + - release + - tip + +script: + - make test + +env: TEST_VERBOSE=1 diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/LICENSE b/Godeps/_workspace/src/github.com/jbenet/go-multihash/LICENSE new file mode 100644 index 0000000..c7386b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/Makefile b/Godeps/_workspace/src/github.com/jbenet/go-multihash/Makefile new file mode 100644 index 0000000..d6e3bf6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/Makefile @@ -0,0 +1,11 @@ +test: go_test other_tests + +other_tests: + cd test && make test + +go_test: go_deps + go test -race -cpu=5 -v ./... + +go_deps: + go get golang.org/x/crypto/sha3 + go get github.com/jbenet/go-base58 diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/README.md b/Godeps/_workspace/src/github.com/jbenet/go-multihash/README.md new file mode 100644 index 0000000..f891499 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/README.md @@ -0,0 +1,45 @@ +# go-multihash + +![travis](https://travis-ci.org/jbenet/go-multihash.svg) + +[multihash](//github.com/jbenet/multihash) implementation in Go. + +## Example + +```go +package main + +import ( + "encoding/hex" + "fmt" + "github.com/jbenet/go-multihash" +) + +func main() { + // ignores errors for simplicity. + // don't do that at home. + + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + mhbuf, _ := multihash.EncodeName(buf, "sha1"); + mhhex := hex.EncodeToString(mhbuf) + fmt.Printf("hex: %v\n", mhhex); + + o, _ := multihash.Decode(mhbuf); + mhhex = hex.EncodeToString(o.Digest); + fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex); +} +``` + +Run [test/foo.go](test/foo.go) + +``` +> cd test/ +> go build +> ./test +hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +``` + +## License + +MIT diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go new file mode 100644 index 0000000..cdd234c --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io.go @@ -0,0 +1,79 @@ +package multihash + +import ( + "fmt" + "io" +) + +// Reader is an io.Reader wrapper that exposes a function +// to read a whole multihash, parse it, and return it. +type Reader interface { + io.Reader + + ReadMultihash() (Multihash, error) +} + +// Writer is an io.Writer wrapper that exposes a function +// to write a whole multihash. +type Writer interface { + io.Writer + + WriteMultihash(Multihash) error +} + +// NewReader wraps an io.Reader with a multihash.Reader +func NewReader(r io.Reader) Reader { + return &mhReader{r} +} + +// NewWriter wraps an io.Writer with a multihash.Writer +func NewWriter(w io.Writer) Writer { + return &mhWriter{w} +} + +type mhReader struct { + r io.Reader +} + +func (r *mhReader) Read(buf []byte) (n int, err error) { + return r.r.Read(buf) +} + +func (r *mhReader) ReadMultihash() (Multihash, error) { + mhhdr := make([]byte, 2) + if _, err := io.ReadFull(r.r, mhhdr); err != nil { + return nil, err + } + + // first byte is the algo, the second is the length. + + // (varints someday...) + length := uint(mhhdr[1]) + + if length > 127 { + return nil, fmt.Errorf("varints not yet supported (length is %d)", length) + } + + buf := make([]byte, length+2) + buf[0] = mhhdr[0] + buf[1] = mhhdr[1] + + if _, err := io.ReadFull(r.r, buf[2:]); err != nil { + return nil, err + } + + return Cast(buf) +} + +type mhWriter struct { + w io.Writer +} + +func (w *mhWriter) Write(buf []byte) (n int, err error) { + return w.w.Write(buf) +} + +func (w *mhWriter) WriteMultihash(m Multihash) error { + _, err := w.w.Write([]byte(m)) + return err +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go new file mode 100644 index 0000000..44da78e --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/io_test.go @@ -0,0 +1,69 @@ +package multihash + +import ( + "bytes" + "io" + "testing" +) + +func TestReader(t *testing.T) { + + var buf bytes.Buffer + + for _, tc := range testCases { + m, err := tc.Multihash() + if err != nil { + t.Fatal(err) + } + + buf.Write([]byte(m)) + } + + r := NewReader(&buf) + + for _, tc := range testCases { + h, err := tc.Multihash() + if err != nil { + t.Fatal(err) + } + + h2, err := r.ReadMultihash() + if err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(h, h2) { + t.Error("h and h2 should be equal") + } + } +} + +func TestWriter(t *testing.T) { + + var buf bytes.Buffer + w := NewWriter(&buf) + + for _, tc := range testCases { + m, err := tc.Multihash() + if err != nil { + t.Error(err) + continue + } + + if err := w.WriteMultihash(m); err != nil { + t.Error(err) + continue + } + + buf2 := make([]byte, len(m)) + if _, err := io.ReadFull(&buf, buf2); err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(m, buf2) { + t.Error("m and buf2 should be equal") + } + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go new file mode 100644 index 0000000..e03fa98 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash.go @@ -0,0 +1,188 @@ +package multihash + +import ( + "encoding/hex" + "errors" + "fmt" + + b58 "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-base58" +) + +// errors +var ( + ErrUnknownCode = errors.New("unknown multihash code") + ErrTooShort = errors.New("multihash too short. must be > 3 bytes") + ErrTooLong = errors.New("multihash too long. must be < 129 bytes") + ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes") +) + +// ErrInconsistentLen is returned when a decoded multihash has an inconsistent length +type ErrInconsistentLen struct { + dm *DecodedMultihash +} + +func (e ErrInconsistentLen) Error() string { + return fmt.Sprintf("multihash length inconsistent: %v", e.dm) +} + +// constants +const ( + SHA1 = 0x11 + SHA2_256 = 0x12 + SHA2_512 = 0x13 + SHA3 = 0x14 + BLAKE2B = 0x40 + BLAKE2S = 0x41 +) + +// Names maps the name of a hash to the code +var Names = map[string]int{ + "sha1": SHA1, + "sha2-256": SHA2_256, + "sha2-512": SHA2_512, + "sha3": SHA3, + "blake2b": BLAKE2B, + "blake2s": BLAKE2S, +} + +// Codes maps a hash code to it's name +var Codes = map[int]string{ + SHA1: "sha1", + SHA2_256: "sha2-256", + SHA2_512: "sha2-512", + SHA3: "sha3", + BLAKE2B: "blake2b", + BLAKE2S: "blake2s", +} + +// DefaultLengths maps a hash code to it's default length +var DefaultLengths = map[int]int{ + SHA1: 20, + SHA2_256: 32, + SHA2_512: 64, + SHA3: 64, + BLAKE2B: 64, + BLAKE2S: 32, +} + +type DecodedMultihash struct { + Code int + Name string + Length int + Digest []byte +} + +type Multihash []byte + +func (m *Multihash) HexString() string { + return hex.EncodeToString([]byte(*m)) +} + +func (m *Multihash) String() string { + return m.HexString() +} + +func FromHexString(s string) (Multihash, error) { + b, err := hex.DecodeString(s) + if err != nil { + return Multihash{}, err + } + + return Cast(b) +} + +func (m Multihash) B58String() string { + return b58.Encode([]byte(m)) +} + +func FromB58String(s string) (m Multihash, err error) { + // panic handler, in case we try accessing bytes incorrectly. + defer func() { + if e := recover(); e != nil { + m = Multihash{} + err = e.(error) + } + }() + + //b58 smells like it can panic... + b := b58.Decode(s) + return Cast(b) +} + +func Cast(buf []byte) (Multihash, error) { + dm, err := Decode(buf) + if err != nil { + return Multihash{}, err + } + + if !ValidCode(dm.Code) { + return Multihash{}, ErrUnknownCode + } + + return Multihash(buf), nil +} + +// Decode a hash from the given Multihash. +func Decode(buf []byte) (*DecodedMultihash, error) { + + if len(buf) < 3 { + return nil, ErrTooShort + } + + if len(buf) > 129 { + return nil, ErrTooLong + } + + dm := &DecodedMultihash{ + Code: int(uint8(buf[0])), + Name: Codes[int(uint8(buf[0]))], + Length: int(uint8(buf[1])), + Digest: buf[2:], + } + + if len(dm.Digest) != dm.Length { + return nil, ErrInconsistentLen{dm} + } + + return dm, nil +} + +// Encode a hash digest along with the specified function code. +// Note: the length is derived from the length of the digest itself. +func Encode(buf []byte, code int) ([]byte, error) { + + if !ValidCode(code) { + return nil, ErrUnknownCode + } + + if len(buf) > 127 { + return nil, ErrLenNotSupported + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(code)) + pre[1] = byte(uint8(len(buf))) + return append(pre, buf...), nil +} + +func EncodeName(buf []byte, name string) ([]byte, error) { + return Encode(buf, Names[name]) +} + +// ValidCode checks whether a multihash code is valid. +func ValidCode(code int) bool { + if AppCode(code) { + return true + } + + if _, ok := Codes[code]; ok { + return true + } + + return false +} + +// AppCode checks whether a multihash code is part of the App range. +func AppCode(code int) bool { + return code >= 0 && code < 0x10 +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/.gitignore b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/.gitignore new file mode 100644 index 0000000..2826f17 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/.gitignore @@ -0,0 +1 @@ +multihash diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/README.md b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/README.md new file mode 100644 index 0000000..e1b5012 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/README.md @@ -0,0 +1,116 @@ +# multihash tool + +The `multihash` tool uses `go-multihash` to hash things much like `shasum`. + +Warning: this is a **multihash** tool! Its digests follow the [multihash](https://github.com/jbenet/multihash) format. + +### Install + +``` +go get github.com/jbenet/go-multihash/multihash +``` + +### Usage + +```sh +> multihash -h +usage: ./multihash [options] [FILE] +Print or check multihash checksums. +With no FILE, or when FILE is -, read standard input. + +Options: + -a="sha2-256": one of: sha1, sha2-256, sha2-512, sha3 (shorthand) + -algorithm="sha2-256": one of: sha1, sha2-256, sha2-512, sha3 + -c="": check checksum matches (shorthand) + -check="": check checksum matches + -e="base58": one of: raw, hex, base58, base64 (shorthand) + -encoding="base58": one of: raw, hex, base58, base64 + -l=-1: checksums length in bits (truncate). -1 is default (shorthand) + -length=-1: checksums length in bits (truncate). -1 is default +``` + +### Examples + +#### Input + +```sh +# from stdin +> multihash < main.go +QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8 + +# from file +> ./multihash main.go +QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8 + +# from stdin "filename" +> multihash - < main.go +QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8 +``` + +#### Algorithms + +```sh +> multihash -a ? +error: algorithm '?' not one of: sha1, sha2-256, sha2-512, sha3 + +> multihash -a sha1 < main.go +5drkbcqJUo6fZVvcZJeVEVWAgndvLm + +> multihash -a sha2-256 < main.go +QmcK3s36goo9v2HYcfTrDKKwxaxmJJ59etodQQFYsL5T5N + +> multihash -a sha2-512 < main.go +8VuDcW4CooyPQA8Cc4eYpwjhyDJZqu5m5ZMDFzWULYsVS8d119JaGeNWsZbZ2ZG2kPtbrMx31MidokCigaD65yUPAs + +> multihash -a sha3 < main.go +8tWDCTfAX24DYmzNixTj2ARJkqwRG736VHx5aJppmqRjhW9QT1EuTgKUmu9Pmunzq292jzPKxb2VxSsTXmjFY1HD3B +``` + +#### Encodings + +```sh +> multihash -e raw < main.go + Ϛ�����I�5 S��WG>���_��]g�����u + +> multihash -e hex < main.go +1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275 + +> multihash -e base64 < main.go +EiDPmqK4o4ubSdE1CVOQBZpXRz6XrOtfyuJdZ6i2/rWCdQ== + +> multihash -e base58 < main.go +Qmf1QjEXDmqBm7RqHKqFGNUyhzUjnX7cmgKMrGzzPceZDQ +``` + +#### Digest Length + +```sh +# we're outputing hex (good byte alignment) to show the codes changing +# notice the multihash code (first 2 chars) differs! +> multihash -e hex -a sha2-256 -l 256 < main.go +1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275 +> multihash -e hex -a sha2-512 -l 256 < main.go +132047a4b6c629f5545f529b0ff461dc09119969f3593186277a1cc7a8ea3560a6f1 +> multihash -e hex -a sha3 -l 256 < main.go +14206b9222a1a47939e665261bd2b5573e55e7988675223adde73c1011066ad66335 + +# notice the multihash length (next 2 chars) differs! +> multihash -e hex -a sha2-256 -l 256 < main.go +1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275 +> multihash -e hex -a sha2-256 -l 200 < main.go +1219cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d +``` + +#### Verify Checksum + +```sh +> multihash -c QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8 < main.go +OK checksums match (-q for no output) + +> multihash -c QmcKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa < main.go +error: computed checksum did not match (-q for no output) + +# works with other arguments too +> multihash -e hex -l 128 -c "12102ffc284a1e82bf51e567c75b2ae6edb9" < main.go +OK checksums match (-q for no output) +``` diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/coding.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/coding.go new file mode 100644 index 0000000..563cd3e --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/coding.go @@ -0,0 +1,40 @@ +package main + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + + base58 "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-base58" + mh "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multihash" +) + +func Decode(encoding, digest string) (mh.Multihash, error) { + switch encoding { + case "raw": + return mh.Cast([]byte(digest)) + case "hex": + return hex.DecodeString(digest) + case "base58": + return base58.Decode(digest), nil + case "base64": + return base64.StdEncoding.DecodeString(digest) + default: + return nil, fmt.Errorf("unknown encoding: %s", encoding) + } +} + +func Encode(encoding string, hash mh.Multihash) (string, error) { + switch encoding { + case "raw": + return string(hash), nil + case "hex": + return hex.EncodeToString(hash), nil + case "base58": + return base58.Encode(hash), nil + case "base64": + return base64.StdEncoding.EncodeToString(hash), nil + default: + return "", fmt.Errorf("unknown encoding: %s", encoding) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/main.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/main.go new file mode 100644 index 0000000..77ebc13 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash/main.go @@ -0,0 +1,208 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + mh "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multihash" +) + +var usage = `usage: %s [options] [FILE] +Print or check multihash checksums. +With no FILE, or when FILE is -, read standard input. + +Options: +` + +// flags +var encodings = []string{"raw", "hex", "base58", "base64"} +var algorithms = []string{"sha1", "sha2-256", "sha2-512", "sha3"} +var encoding string +var algorithm string +var algorithmCode int +var length int +var checkRaw string +var checkMh mh.Multihash +var inputFilename string +var quiet bool + +// joined names +var algoStr string +var encStr string + +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, usage, os.Args[0]) + flag.PrintDefaults() + } + + algoStr = "one of: " + strings.Join(algorithms, ", ") + flag.StringVar(&algorithm, "algorithm", "sha2-256", algoStr) + flag.StringVar(&algorithm, "a", "sha2-256", algoStr+" (shorthand)") + + encStr = "one of: " + strings.Join(encodings, ", ") + flag.StringVar(&encoding, "encoding", "base58", encStr) + flag.StringVar(&encoding, "e", "base58", encStr+" (shorthand)") + + checkStr := "check checksum matches" + flag.StringVar(&checkRaw, "check", "", checkStr) + flag.StringVar(&checkRaw, "c", "", checkStr+" (shorthand)") + + lengthStr := "checksums length in bits (truncate). -1 is default" + flag.IntVar(&length, "length", -1, lengthStr) + flag.IntVar(&length, "l", -1, lengthStr+" (shorthand)") + + quietStr := "quiet output (no newline on checksum, no error text)" + flag.BoolVar(&quiet, "quiet", false, quietStr) + flag.BoolVar(&quiet, "q", false, quietStr+" (shorthand)") +} + +func strIn(a string, set []string) bool { + for _, s := range set { + if s == a { + return true + } + } + return false +} + +func parseFlags() error { + flag.Parse() + + if !strIn(algorithm, algorithms) { + return fmt.Errorf("algorithm '%s' not %s", algorithm, algoStr) + } + var found bool + algorithmCode, found = mh.Names[algorithm] + if !found { + return fmt.Errorf("algorithm '%s' not found (lib error, pls report).") + } + + if !strIn(encoding, encodings) { + return fmt.Errorf("encoding '%s' not %s", encoding, encStr) + } + + if checkRaw != "" { + var err error + checkMh, err = Decode(encoding, checkRaw) + if err != nil { + return fmt.Errorf("fail to decode check '%s': %s", checkRaw, err) + } + } + + if length >= 0 { + if length%8 != 0 { + return fmt.Errorf("length must be multiple of 8") + } + length = length / 8 + + if length > mh.DefaultLengths[algorithmCode] { + length = mh.DefaultLengths[algorithmCode] + } + } + return nil +} + +func getInput() (io.ReadCloser, error) { + args := flag.Args() + + switch { + case len(args) < 1: + inputFilename = "-" + return os.Stdin, nil + case args[0] == "-": + inputFilename = "-" + return os.Stdin, nil + default: + inputFilename = args[0] + f, err := os.Open(args[0]) + if err != nil { + return nil, fmt.Errorf("failed to open '%s': %s", args[0], err) + } + return f, nil + } +} + +func check(h1 mh.Multihash, r io.Reader) error { + h2, err := hash(r) + if err != nil { + return err + } + + if !bytes.Equal(h1, h2) { + if quiet { + os.Exit(1) + } + return fmt.Errorf("computed checksum did not match (-q for no output)") + } + + if !quiet { + fmt.Println("OK checksums match (-q for no output)") + } + return nil +} + +func hash(r io.Reader) (mh.Multihash, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return mh.Sum(b, algorithmCode, length) +} + +func printHash(r io.Reader) error { + h, err := hash(r) + if err != nil { + return err + } + + s, err := Encode(encoding, h) + if err != nil { + return err + } + + if quiet { + fmt.Print(s) + } else { + fmt.Println(s) + } + return nil +} + +func main() { + checkErr := func(err error) { + if err != nil { + die("error: ", err) + } + } + + err := parseFlags() + checkErr(err) + + inp, err := getInput() + checkErr(err) + + if checkMh != nil { + err = check(checkMh, inp) + checkErr(err) + } else { + err = printHash(inp) + checkErr(err) + } + inp.Close() +} + +func die(v ...interface{}) { + if !quiet { + fmt.Fprint(os.Stderr, v...) + fmt.Fprint(os.Stderr, "\n") + } + // flag.Usage() + os.Exit(1) +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go new file mode 100644 index 0000000..dfa858e --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/multihash_test.go @@ -0,0 +1,270 @@ +package multihash + +import ( + "bytes" + "encoding/hex" + "fmt" + "testing" +) + +// maybe silly, but makes it so changing +// the table accidentally has to happen twice. +var tCodes = map[int]string{ + 0x11: "sha1", + 0x12: "sha2-256", + 0x13: "sha2-512", + 0x14: "sha3", + 0x40: "blake2b", + 0x41: "blake2s", +} + +type TestCase struct { + hex string + code int + name string +} + +var testCases = []TestCase{ + TestCase{"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33", 0x11, "sha1"}, + TestCase{"0beec7b5", 0x11, "sha1"}, + TestCase{"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", 0x12, "sha2-256"}, + TestCase{"2c26b46b", 0x12, "sha2-256"}, + TestCase{"0beec7b5ea3f0fdbc9", 0x40, "blake2b"}, +} + +func (tc TestCase) Multihash() (Multihash, error) { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + return nil, err + } + + b := make([]byte, 2+len(ob)) + b[0] = byte(uint8(tc.code)) + b[1] = byte(uint8(len(ob))) + copy(b[2:], ob) + return Cast(b) +} + +func TestEncode(t *testing.T) { + for _, tc := range testCases { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + t.Error(err) + continue + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + encC, err := Encode(ob, tc.code) + if err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(encC, nb) { + t.Error("encoded byte mismatch: ", encC, nb) + } + + encN, err := EncodeName(ob, tc.name) + if err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(encN, nb) { + t.Error("encoded byte mismatch: ", encN, nb) + } + + h, err := tc.Multihash() + if err != nil { + t.Error(err) + } + if !bytes.Equal(h, nb) { + t.Error("Multihash func mismatch.") + } + } +} + +func ExampleEncodeName() { + // ignores errors for simplicity - don't do that at home. + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + mhbuf, _ := EncodeName(buf, "sha1") + mhhex := hex.EncodeToString(mhbuf) + fmt.Printf("hex: %v\n", mhhex) + + // Output: + // hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +} + +func TestDecode(t *testing.T) { + for _, tc := range testCases { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + t.Error(err) + continue + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + dec, err := Decode(nb) + if err != nil { + t.Error(err) + continue + } + + if dec.Code != tc.code { + t.Error("decoded code mismatch: ", dec.Code, tc.code) + } + + if dec.Name != tc.name { + t.Error("decoded name mismatch: ", dec.Name, tc.name) + } + + if dec.Length != len(ob) { + t.Error("decoded length mismatch: ", dec.Length, len(ob)) + } + + if !bytes.Equal(dec.Digest, ob) { + t.Error("decoded byte mismatch: ", dec.Digest, ob) + } + } +} + +func TestTable(t *testing.T) { + for k, v := range tCodes { + if Codes[k] != v { + t.Error("Table mismatch: ", Codes[k], v) + } + if Names[v] != k { + t.Error("Table mismatch: ", Names[v], k) + } + } +} + +func ExampleDecode() { + // ignores errors for simplicity - don't do that at home. + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + mhbuf, _ := EncodeName(buf, "sha1") + o, _ := Decode(mhbuf) + mhhex := hex.EncodeToString(o.Digest) + fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex) + + // Output: + // obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 +} + +func TestValidCode(t *testing.T) { + for i := 0; i < 0xff; i++ { + _, ok := tCodes[i] + b := AppCode(i) || ok + + if ValidCode(i) != b { + t.Error("ValidCode incorrect for: ", i) + } + } +} + +func TestAppCode(t *testing.T) { + for i := 0; i < 0xff; i++ { + b := i >= 0 && i < 0x10 + if AppCode(i) != b { + t.Error("AppCode incorrect for: ", i) + } + } +} + +func TestCast(t *testing.T) { + for _, tc := range testCases { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + t.Error(err) + continue + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + if _, err := Cast(nb); err != nil { + t.Error(err) + continue + } + + if _, err = Cast(ob); err == nil { + t.Error("cast failed to detect non-multihash") + continue + } + } +} + +func TestHex(t *testing.T) { + for _, tc := range testCases { + ob, err := hex.DecodeString(tc.hex) + if err != nil { + t.Error(err) + continue + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + hs := hex.EncodeToString(nb) + mh, err := FromHexString(hs) + if err != nil { + t.Error(err) + continue + } + + if !bytes.Equal(mh, nb) { + t.Error("FromHexString failed", nb, mh) + continue + } + + if mh.HexString() != hs { + t.Error("Multihash.HexString failed", hs, mh.HexString) + continue + } + } +} + +func BenchmarkEncode(b *testing.B) { + tc := testCases[0] + ob, err := hex.DecodeString(tc.hex) + if err != nil { + b.Error(err) + return + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(ob, tc.code) + } +} + +func BenchmarkDecode(b *testing.B) { + tc := testCases[0] + ob, err := hex.DecodeString(tc.hex) + if err != nil { + b.Error(err) + return + } + + pre := make([]byte, 2) + pre[0] = byte(uint8(tc.code)) + pre[1] = byte(uint8(len(ob))) + nb := append(pre, ob...) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(nb) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go new file mode 100644 index 0000000..370ebd6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum.go @@ -0,0 +1,72 @@ +package multihash + +import ( + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "errors" + "fmt" + + sha3 "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/golang.org/x/crypto/sha3" +) + +var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.") + +func Sum(data []byte, code int, length int) (Multihash, error) { + m := Multihash{} + err := error(nil) + if !ValidCode(code) { + return m, fmt.Errorf("invalid multihash code %d", code) + } + + var d []byte + switch code { + case SHA1: + d = sumSHA1(data) + case SHA2_256: + d = sumSHA256(data) + case SHA2_512: + d = sumSHA512(data) + case SHA3: + d, err = sumSHA3(data) + default: + return m, ErrSumNotSupported + } + + if err != nil { + return m, err + } + + if length < 0 { + var ok bool + length, ok = DefaultLengths[code] + if !ok { + return m, fmt.Errorf("no default length for code %d", code) + } + } + + return Encode(d[0:length], code) +} + +func sumSHA1(data []byte) []byte { + a := sha1.Sum(data) + return a[0:20] +} + +func sumSHA256(data []byte) []byte { + a := sha256.Sum256(data) + return a[0:32] +} + +func sumSHA512(data []byte) []byte { + a := sha512.Sum512(data) + return a[0:64] +} + +func sumSHA3(data []byte) ([]byte, error) { + h := sha3.New512() + if _, err := h.Write(data); err != nil { + return nil, err + } + return h.Sum(nil), nil +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go new file mode 100644 index 0000000..1e5891b --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/sum_test.go @@ -0,0 +1,66 @@ +package multihash + +import ( + "bytes" + "testing" +) + +type SumTestCase struct { + code int + length int + input string + hex string +} + +var sumTestCases = []SumTestCase{ + SumTestCase{SHA1, -1, "foo", "11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"}, + SumTestCase{SHA1, 10, "foo", "110a0beec7b5ea3f0fdbc95d"}, + SumTestCase{SHA2_256, -1, "foo", "12202c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"}, + SumTestCase{SHA2_256, 16, "foo", "12102c26b46b68ffc68ff99b453c1d304134"}, + SumTestCase{SHA2_512, -1, "foo", "1340f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7"}, + SumTestCase{SHA2_512, 32, "foo", "1320f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc663832"}, +} + +func TestSum(t *testing.T) { + + for _, tc := range sumTestCases { + + m1, err := FromHexString(tc.hex) + if err != nil { + t.Error(err) + continue + } + + m2, err := Sum([]byte(tc.input), tc.code, tc.length) + if err != nil { + t.Error(tc.code, "sum failed.", err) + continue + } + + if !bytes.Equal(m1, m2) { + t.Error(tc.code, "sum failed.", m1, m2) + } + + s1 := m1.HexString() + if s1 != tc.hex { + t.Error("hex strings not the same") + } + + s2 := m1.B58String() + m3, err := FromB58String(s2) + if err != nil { + t.Error("failed to decode b58") + } else if !bytes.Equal(m3, m1) { + t.Error("b58 failing bytes") + } else if s2 != m3.B58String() { + t.Error("b58 failing string") + } + } +} + +func BenchmarkSum(b *testing.B) { + tc := sumTestCases[0] + for i := 0; i < b.N; i++ { + Sum([]byte(tc.input), tc.code, tc.length) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/.gitignore b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/.gitignore new file mode 100644 index 0000000..27f75ed --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/.gitignore @@ -0,0 +1 @@ +bin/multihash diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/Makefile b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/Makefile new file mode 100644 index 0000000..e9660d0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/Makefile @@ -0,0 +1,25 @@ +BINS = bin/multihash +MULTIHASH_ROOT = ../ +MULTIHASH_CMD = ../multihash + +all: deps + +deps: bins + +clean: + rm $(BINS) + +bins: $(BINS) + +bin/multihash: $(MULTIHASH_ROOT)/**/*.go + go build -o bin/multihash $(MULTIHASH_CMD) + +test: test_expensive + +test_expensive: + cd sharness && make TEST_EXPENSIVE=1 + +test_cheap: + cd sharness && make + +.PHONY: all clean diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/.gitignore b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/.gitignore new file mode 100644 index 0000000..5e59048 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/.gitignore @@ -0,0 +1,3 @@ +lib/sharness/ +test-results/ +trash directory.*.sh/ diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/Makefile b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/Makefile new file mode 100644 index 0000000..ad806a0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/Makefile @@ -0,0 +1,37 @@ +# Run tests +# +# Copyright (c) 2014 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# + +# NOTE: run with TEST_VERBOSE=1 for verbose sharness tests. + +T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) +BINS = bin/multihash +SHARNESS = lib/sharness/sharness.sh + +all: clean deps $(T) aggregate + +clean: + @echo "*** $@ ***" + -rm -rf test-results + +$(T): + @echo "*** $@ ***" + ./$@ + +aggregate: + @echo "*** $@ ***" + lib/test-aggregate-results.sh + +deps: $(SHARNESS) $(BINS) + +$(SHARNESS): + @echo "*** installing $@ ***" + lib/install-sharness.sh + +bin/%: + @echo "*** installing $@ ***" + cd .. && make $@ + +.PHONY: all clean $(T) aggregate diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/bin b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/bin new file mode 120000 index 0000000..19f285a --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/bin @@ -0,0 +1 @@ +../bin \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/install-sharness.sh b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/install-sharness.sh new file mode 100644 index 0000000..d573c40 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/install-sharness.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# install sharness.sh +# +# Copyright (c) 2014 Juan Batiz-Benet +# MIT Licensed; see the LICENSE file in this repository. +# + +# settings +version=50229a79ba22b2f13ccd82451d86570fecbd194c +urlprefix=https://github.com/mlafeldt/sharness.git +clonedir=lib +sharnessdir=sharness + +die() { + echo >&2 "$@" + exit 1 +} + +mkdir -p "$clonedir" || die "Could not create '$clonedir' directory" +cd "$clonedir" || die "Could not cd into '$clonedir' directory" + +git clone "$urlprefix" || die "Could not clone '$urlprefix'" +cd "$sharnessdir" || die "Could not cd into '$sharnessdir' directory" +git checkout "$version" || die "Could not checkout '$version'" + +exit 0 diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-aggregate-results.sh b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-aggregate-results.sh new file mode 100644 index 0000000..c2ff76c --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-aggregate-results.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# +# Script to aggregate results using Sharness +# +# Copyright (c) 2014 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# + +SHARNESS_AGGREGATE="lib/sharness/aggregate-results.sh" + +test -f "$SHARNESS_AGGREGATE" || { + echo >&2 "Cannot find: $SHARNESS_AGGREGATE" + echo >&2 "Please check Sharness installation." + exit 1 +} + +ls test-results/t*-*.sh.*.counts | "$SHARNESS_AGGREGATE" diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-lib.sh b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-lib.sh new file mode 100644 index 0000000..988164a --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/lib/test-lib.sh @@ -0,0 +1,43 @@ +# Test framework for go-ipfs +# +# Copyright (c) 2014 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# +# We are using sharness (https://github.com/mlafeldt/sharness) +# which was extracted from the Git test framework. + +# Use the multihash tool to test against + +# Add current directory to path, for multihash tool. +PATH=$(pwd)/bin:${PATH} + +# Set sharness verbosity. we set the env var directly as +# it's too late to pass in --verbose, and --verbose is harder +# to pass through in some cases. +test "$TEST_VERBOSE" = 1 && verbose=t + +# assert the `multihash` we're using is the right one. +if test `which multihash` != $(pwd)/bin/multihash; then + echo >&2 "Cannot find the tests' local multihash tool." + echo >&2 "Please check test and multihash tool installation." + exit 1 +fi + +SHARNESS_LIB="lib/sharness/sharness.sh" + +. "$SHARNESS_LIB" || { + echo >&2 "Cannot source: $SHARNESS_LIB" + echo >&2 "Please check Sharness installation." + exit 1 +} + +# Please put go-multihash specific shell functions below + +for hashbin in sha1sum shasum; do + if type "$hashbin"; then + export SHASUMBIN="$hashbin" && + test_set_prereq SHASUM && + break + fi +done + diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0010-basics.sh b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0010-basics.sh new file mode 100644 index 0000000..1ff86ff --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0010-basics.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2015 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# + +test_description="Basic tests" + +. lib/test-lib.sh + +test_expect_success "current dir is writable" ' + echo "It works!" >test.txt +' + +test_expect_success "multihash is available" ' + type multihash +' + +test_expect_success "multihash help output looks good" ' + test_must_fail multihash -h 2>help.txt && + cat help.txt | egrep -i "^usage:" >/dev/null && + cat help.txt | egrep -i "multihash .*options.*file" >/dev/null +' + +test_done diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0020-sha1.sh b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0020-sha1.sh new file mode 100644 index 0000000..a5be58b --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multihash/test/sharness/t0020-sha1.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright (c) 2015 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# + +test_description="sha1 tests" + +. lib/test-lib.sh + +test_expect_success "setup sha1 tests" ' + echo "Hash me!" >hash_me.txt && + SHA1=bc6f2c3cd945bc754789e50b2f68deee2f421810 && + echo "1114$SHA1" >actual +' + +test_expect_success "'multihash -a=sha1 -e=hex' works" ' + multihash -a=sha1 -e=hex hash_me.txt >expected +' + +test_expect_success "'multihash -a=sha1 -e=hex' output looks good" ' + test_cmp expected actual +' + +test_expect_success SHASUM "check hash using shasum" ' + echo "$SHA1 hash_me.txt" >actual && + $SHASUMBIN hash_me.txt >expected && + test_cmp expected actual +' + +test_done diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/doc.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/doc.go new file mode 100644 index 0000000..3dab530 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/doc.go @@ -0,0 +1,66 @@ +// Copyright 2014 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 sha3 implements the SHA-3 fixed-output-length hash functions and +// the SHAKE variable-output-length hash functions defined by FIPS-202. +// +// Both types of hash function use the "sponge" construction and the Keccak +// permutation. For a detailed specification see http://keccak.noekeon.org/ +// +// +// Guidance +// +// If you aren't sure what function you need, use SHAKE256 with at least 64 +// bytes of output. The SHAKE instances are faster than the SHA3 instances; +// the latter have to allocate memory to conform to the hash.Hash interface. +// +// If you need a secret-key MAC (message authentication code), prepend the +// secret key to the input, hash with SHAKE256 and read at least 32 bytes of +// output. +// +// +// Security strengths +// +// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security +// strength against preimage attacks of x bits. Since they only produce "x" +// bits of output, their collision-resistance is only "x/2" bits. +// +// The SHAKE-256 and -128 functions have a generic security strength of 256 and +// 128 bits against all attacks, provided that at least 2x bits of their output +// is used. Requesting more than 64 or 32 bytes of output, respectively, does +// not increase the collision-resistance of the SHAKE functions. +// +// +// The sponge construction +// +// A sponge builds a pseudo-random function from a public pseudo-random +// permutation, by applying the permutation to a state of "rate + capacity" +// bytes, but hiding "capacity" of the bytes. +// +// A sponge starts out with a zero state. To hash an input using a sponge, up +// to "rate" bytes of the input are XORed into the sponge's state. The sponge +// is then "full" and the permutation is applied to "empty" it. This process is +// repeated until all the input has been "absorbed". The input is then padded. +// The digest is "squeezed" from the sponge in the same way, except that output +// output is copied out instead of input being XORed in. +// +// A sponge is parameterized by its generic security strength, which is equal +// to half its capacity; capacity + rate is equal to the permutation's width. +// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means +// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2. +// +// +// Recommendations +// +// The SHAKE functions are recommended for most new uses. They can produce +// output of arbitrary length. SHAKE256, with an output length of at least +// 64 bytes, provides 256-bit security against all attacks. The Keccak team +// recommends it for most applications upgrading from SHA2-512. (NIST chose a +// much stronger, but much slower, sponge instance for SHA3-512.) +// +// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions. +// They produce output of the same length, with the same security strengths +// against all attacks. This means, in particular, that SHA3-256 only has +// 128-bit collision resistance, because its output length is 32 bytes. +package sha3 diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/hashes.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/hashes.go new file mode 100644 index 0000000..2b51cf4 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/hashes.go @@ -0,0 +1,65 @@ +// Copyright 2014 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 sha3 + +// This file provides functions for creating instances of the SHA-3 +// and SHAKE hash functions, as well as utility functions for hashing +// bytes. + +import ( + "hash" +) + +// New224 creates a new SHA3-224 hash. +// Its generic security strength is 224 bits against preimage attacks, +// and 112 bits against collision attacks. +func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} } + +// New256 creates a new SHA3-256 hash. +// Its generic security strength is 256 bits against preimage attacks, +// and 128 bits against collision attacks. +func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} } + +// New384 creates a new SHA3-384 hash. +// Its generic security strength is 384 bits against preimage attacks, +// and 192 bits against collision attacks. +func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} } + +// New512 creates a new SHA3-512 hash. +// Its generic security strength is 512 bits against preimage attacks, +// and 256 bits against collision attacks. +func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} } + +// Sum224 returns the SHA3-224 digest of the data. +func Sum224(data []byte) (digest [28]byte) { + h := New224() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum256 returns the SHA3-256 digest of the data. +func Sum256(data []byte) (digest [32]byte) { + h := New256() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum384 returns the SHA3-384 digest of the data. +func Sum384(data []byte) (digest [48]byte) { + h := New384() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum512 returns the SHA3-512 digest of the data. +func Sum512(data []byte) (digest [64]byte) { + h := New512() + h.Write(data) + h.Sum(digest[:0]) + return +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/keccakf.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/keccakf.go new file mode 100644 index 0000000..13e7058 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/keccakf.go @@ -0,0 +1,410 @@ +// Copyright 2014 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 sha3 + +// rc stores the round constants for use in the ι step. +var rc = [24]uint64{ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +} + +// keccakF1600 applies the Keccak permutation to a 1600b-wide +// state represented as a slice of 25 uint64s. +func keccakF1600(a *[25]uint64) { + // Implementation translated from Keccak-inplace.c + // in the keccak reference code. + var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 + + for i := 0; i < 24; i += 4 { + // Combines the 5 steps in each round into 2 steps. + // Unrolls 4 rounds per loop and spreads some steps across rounds. + + // Round 1 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[6] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[12] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[18] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[24] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i] + a[6] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[16] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[22] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[3] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[10] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[1] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[7] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[19] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[20] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[11] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[23] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[4] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[5] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[2] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[8] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[14] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[15] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + // Round 2 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[16] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[7] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[23] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[14] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1] + a[16] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[11] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[2] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[18] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[20] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[6] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[22] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[4] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[15] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[1] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[8] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[24] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[10] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[12] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[3] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[19] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[5] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + // Round 3 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[11] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[22] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[8] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[19] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2] + a[11] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[1] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[12] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[23] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[15] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[16] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[2] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[24] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[5] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[6] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[3] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[14] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[20] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[7] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[18] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[4] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[10] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + // Round 4 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[1] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[2] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[3] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[4] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3] + a[1] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[6] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[7] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[8] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[5] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[11] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[12] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[14] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[10] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[16] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[18] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[19] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[15] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[22] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[23] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[24] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[20] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/register.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/register.go new file mode 100644 index 0000000..3cf6a22 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/register.go @@ -0,0 +1,18 @@ +// Copyright 2014 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. + +// +build go1.4 + +package sha3 + +import ( + "crypto" +) + +func init() { + crypto.RegisterHash(crypto.SHA3_224, New224) + crypto.RegisterHash(crypto.SHA3_256, New256) + crypto.RegisterHash(crypto.SHA3_384, New384) + crypto.RegisterHash(crypto.SHA3_512, New512) +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3.go new file mode 100644 index 0000000..c8fd31c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3.go @@ -0,0 +1,193 @@ +// Copyright 2014 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 sha3 + +// spongeDirection indicates the direction bytes are flowing through the sponge. +type spongeDirection int + +const ( + // spongeAbsorbing indicates that the sponge is absorbing input. + spongeAbsorbing spongeDirection = iota + // spongeSqueezing indicates that the sponge is being squeezed. + spongeSqueezing +) + +const ( + // maxRate is the maximum size of the internal buffer. SHAKE-256 + // currently needs the largest buffer. + maxRate = 168 +) + +type state struct { + // Generic sponge components. + a [25]uint64 // main state of the hash + buf []byte // points into storage + rate int // the number of bytes of state to use + + // dsbyte contains the "domain separation" bits and the first bit of + // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the + // SHA-3 and SHAKE functions by appending bitstrings to the message. + // Using a little-endian bit-ordering convention, these are "01" for SHA-3 + // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the + // padding rule from section 5.1 is applied to pad the message to a multiple + // of the rate, which involves adding a "1" bit, zero or more "0" bits, and + // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, + // giving 00000110b (0x06) and 00011111b (0x1f). + // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf + // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and + // Extendable-Output Functions (May 2014)" + dsbyte byte + storage [maxRate]byte + + // Specific to SHA-3 and SHAKE. + fixedOutput bool // whether this is a fixed-ouput-length instance + outputLen int // the default output size in bytes + state spongeDirection // whether the sponge is absorbing or squeezing +} + +// BlockSize returns the rate of sponge underlying this hash function. +func (d *state) BlockSize() int { return d.rate } + +// Size returns the output size of the hash function in bytes. +func (d *state) Size() int { return d.outputLen } + +// Reset clears the internal state by zeroing the sponge state and +// the byte buffer, and setting Sponge.state to absorbing. +func (d *state) Reset() { + // Zero the permutation's state. + for i := range d.a { + d.a[i] = 0 + } + d.state = spongeAbsorbing + d.buf = d.storage[:0] +} + +func (d *state) clone() *state { + ret := *d + if ret.state == spongeAbsorbing { + ret.buf = ret.storage[:len(ret.buf)] + } else { + ret.buf = ret.storage[d.rate-cap(d.buf) : d.rate] + } + + return &ret +} + +// permute applies the KeccakF-1600 permutation. It handles +// any input-output buffering. +func (d *state) permute() { + switch d.state { + case spongeAbsorbing: + // If we're absorbing, we need to xor the input into the state + // before applying the permutation. + xorIn(d, d.buf) + d.buf = d.storage[:0] + keccakF1600(&d.a) + case spongeSqueezing: + // If we're squeezing, we need to apply the permutatin before + // copying more output. + keccakF1600(&d.a) + d.buf = d.storage[:d.rate] + copyOut(d, d.buf) + } +} + +// pads appends the domain separation bits in dsbyte, applies +// the multi-bitrate 10..1 padding rule, and permutes the state. +func (d *state) padAndPermute(dsbyte byte) { + if d.buf == nil { + d.buf = d.storage[:0] + } + // Pad with this instance's domain-separator bits. We know that there's + // at least one byte of space in d.buf because, if it were full, + // permute would have been called to empty it. dsbyte also contains the + // first one bit for the padding. See the comment in the state struct. + d.buf = append(d.buf, dsbyte) + zerosStart := len(d.buf) + d.buf = d.storage[:d.rate] + for i := zerosStart; i < d.rate; i++ { + d.buf[i] = 0 + } + // This adds the final one bit for the padding. Because of the way that + // bits are numbered from the LSB upwards, the final bit is the MSB of + // the last byte. + d.buf[d.rate-1] ^= 0x80 + // Apply the permutation + d.permute() + d.state = spongeSqueezing + d.buf = d.storage[:d.rate] + copyOut(d, d.buf) +} + +// Write absorbs more data into the hash's state. It produces an error +// if more data is written to the ShakeHash after writing +func (d *state) Write(p []byte) (written int, err error) { + if d.state != spongeAbsorbing { + panic("sha3: write to sponge after read") + } + if d.buf == nil { + d.buf = d.storage[:0] + } + written = len(p) + + for len(p) > 0 { + if len(d.buf) == 0 && len(p) >= d.rate { + // The fast path; absorb a full "rate" bytes of input and apply the permutation. + xorIn(d, p[:d.rate]) + p = p[d.rate:] + keccakF1600(&d.a) + } else { + // The slow path; buffer the input until we can fill the sponge, and then xor it in. + todo := d.rate - len(d.buf) + if todo > len(p) { + todo = len(p) + } + d.buf = append(d.buf, p[:todo]...) + p = p[todo:] + + // If the sponge is full, apply the permutation. + if len(d.buf) == d.rate { + d.permute() + } + } + } + + return +} + +// Read squeezes an arbitrary number of bytes from the sponge. +func (d *state) Read(out []byte) (n int, err error) { + // If we're still absorbing, pad and apply the permutation. + if d.state == spongeAbsorbing { + d.padAndPermute(d.dsbyte) + } + + n = len(out) + + // Now, do the squeezing. + for len(out) > 0 { + n := copy(out, d.buf) + d.buf = d.buf[n:] + out = out[n:] + + // Apply the permutation if we've squeezed the sponge dry. + if len(d.buf) == 0 { + d.permute() + } + } + + return +} + +// Sum applies padding to the hash state and then squeezes out the desired +// number of output bytes. +func (d *state) Sum(in []byte) []byte { + // Make a copy of the original hash so that caller can keep writing + // and summing. + dup := d.clone() + hash := make([]byte, dup.outputLen) + dup.Read(hash) + return append(in, hash...) +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3_test.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3_test.go new file mode 100644 index 0000000..cbf811d --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/sha3_test.go @@ -0,0 +1,306 @@ +// Copyright 2014 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 sha3 + +// Tests include all the ShortMsgKATs provided by the Keccak team at +// https://github.com/gvanas/KeccakCodePackage +// +// They only include the zero-bit case of the bitwise testvectors +// published by NIST in the draft of FIPS-202. + +import ( + "bytes" + "compress/flate" + "encoding/hex" + "encoding/json" + "hash" + "os" + "strings" + "testing" +) + +const ( + testString = "brekeccakkeccak koax koax" + katFilename = "testdata/keccakKats.json.deflate" +) + +// Internal-use instances of SHAKE used to test against KATs. +func newHashShake128() hash.Hash { + return &state{rate: 168, dsbyte: 0x1f, outputLen: 512} +} +func newHashShake256() hash.Hash { + return &state{rate: 136, dsbyte: 0x1f, outputLen: 512} +} + +// testDigests contains functions returning hash.Hash instances +// with output-length equal to the KAT length for both SHA-3 and +// SHAKE instances. +var testDigests = map[string]func() hash.Hash{ + "SHA3-224": New224, + "SHA3-256": New256, + "SHA3-384": New384, + "SHA3-512": New512, + "SHAKE128": newHashShake128, + "SHAKE256": newHashShake256, +} + +// testShakes contains functions that return ShakeHash instances for +// testing the ShakeHash-specific interface. +var testShakes = map[string]func() ShakeHash{ + "SHAKE128": NewShake128, + "SHAKE256": NewShake256, +} + +// decodeHex converts a hex-encoded string into a raw byte string. +func decodeHex(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +// structs used to marshal JSON test-cases. +type KeccakKats struct { + Kats map[string][]struct { + Digest string `json:"digest"` + Length int64 `json:"length"` + Message string `json:"message"` + } +} + +func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) { + xorInOrig, copyOutOrig := xorIn, copyOut + xorIn, copyOut = xorInGeneric, copyOutGeneric + testf("generic") + if xorImplementationUnaligned != "generic" { + xorIn, copyOut = xorInUnaligned, copyOutUnaligned + testf("unaligned") + } + xorIn, copyOut = xorInOrig, copyOutOrig +} + +// TestKeccakKats tests the SHA-3 and Shake implementations against all the +// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage +// (The testvectors are stored in keccakKats.json.deflate due to their length.) +func TestKeccakKats(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + // Read the KATs. + deflated, err := os.Open(katFilename) + if err != nil { + t.Errorf("error opening %s: %s", katFilename, err) + } + file := flate.NewReader(deflated) + dec := json.NewDecoder(file) + var katSet KeccakKats + err = dec.Decode(&katSet) + if err != nil { + t.Errorf("error decoding KATs: %s", err) + } + + // Do the KATs. + for functionName, kats := range katSet.Kats { + d := testDigests[functionName]() + for _, kat := range kats { + d.Reset() + in, err := hex.DecodeString(kat.Message) + if err != nil { + t.Errorf("error decoding KAT: %s", err) + } + d.Write(in[:kat.Length/8]) + got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) + if got != kat.Digest { + t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s", + functionName, impl, kat.Length, kat.Message, got, kat.Digest) + t.Logf("wanted %+v", kat) + t.FailNow() + } + continue + } + } + }) +} + +// TestUnalignedWrite tests that writing data in an arbitrary pattern with +// small input buffers. +func testUnalignedWrite(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + buf := sequentialBytes(0x10000) + for alg, df := range testDigests { + d := df() + d.Reset() + d.Write(buf) + want := d.Sum(nil) + d.Reset() + for i := 0; i < len(buf); { + // Cycle through offsets which make a 137 byte sequence. + // Because 137 is prime this sequence should exercise all corner cases. + offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} + for _, j := range offsets { + if v := len(buf) - i; v < j { + j = v + } + d.Write(buf[i : i+j]) + i += j + } + } + got := d.Sum(nil) + if !bytes.Equal(got, want) { + t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want) + } + } + }) +} + +// TestAppend checks that appending works when reallocation is necessary. +func TestAppend(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + d := New224() + + for capacity := 2; capacity < 64; capacity += 64 { + // The first time around the loop, Sum will have to reallocate. + // The second time, it will not. + buf := make([]byte, 2, capacity) + d.Reset() + d.Write([]byte{0xcc}) + buf = d.Sum(buf) + expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" + if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { + t.Errorf("got %s, want %s", got, expected) + } + } + }) +} + +// TestAppendNoRealloc tests that appending works when no reallocation is necessary. +func TestAppendNoRealloc(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + buf := make([]byte, 1, 200) + d := New224() + d.Write([]byte{0xcc}) + buf = d.Sum(buf) + expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" + if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { + t.Errorf("%s: got %s, want %s", impl, got, expected) + } + }) +} + +// TestSqueezing checks that squeezing the full output a single time produces +// the same output as repeatedly squeezing the instance. +func TestSqueezing(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + for functionName, newShakeHash := range testShakes { + d0 := newShakeHash() + d0.Write([]byte(testString)) + ref := make([]byte, 32) + d0.Read(ref) + + d1 := newShakeHash() + d1.Write([]byte(testString)) + var multiple []byte + for _ = range ref { + one := make([]byte, 1) + d1.Read(one) + multiple = append(multiple, one...) + } + if !bytes.Equal(ref, multiple) { + t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref)) + } + } + }) +} + +// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. +func sequentialBytes(size int) []byte { + result := make([]byte, size) + for i := range result { + result[i] = byte(i) + } + return result +} + +// BenchmarkPermutationFunction measures the speed of the permutation function +// with no input data. +func BenchmarkPermutationFunction(b *testing.B) { + b.SetBytes(int64(200)) + var lanes [25]uint64 + for i := 0; i < b.N; i++ { + keccakF1600(&lanes) + } +} + +// benchmarkHash tests the speed to hash num buffers of buflen each. +func benchmarkHash(b *testing.B, h hash.Hash, size, num int) { + b.StopTimer() + h.Reset() + data := sequentialBytes(size) + b.SetBytes(int64(size * num)) + b.StartTimer() + + var state []byte + for i := 0; i < b.N; i++ { + for j := 0; j < num; j++ { + h.Write(data) + } + state = h.Sum(state[:0]) + } + b.StopTimer() + h.Reset() +} + +// benchmarkShake is specialized to the Shake instances, which don't +// require a copy on reading output. +func benchmarkShake(b *testing.B, h ShakeHash, size, num int) { + b.StopTimer() + h.Reset() + data := sequentialBytes(size) + d := make([]byte, 32) + + b.SetBytes(int64(size * num)) + b.StartTimer() + + for i := 0; i < b.N; i++ { + h.Reset() + for j := 0; j < num; j++ { + h.Write(data) + } + h.Read(d) + } +} + +func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) } +func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) } +func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) } +func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) } + +func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) } +func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) } +func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) } +func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) } + +func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) } + +func Example_sum() { + buf := []byte("some data to hash") + // A hash needs to be 64 bytes long to have 256-bit collision resistance. + h := make([]byte, 64) + // Compute a 64-byte hash of buf and put it in h. + ShakeSum256(h, buf) +} + +func Example_mac() { + k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long") + buf := []byte("and this is some data to authenticate") + // A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key. + h := make([]byte, 32) + d := NewShake256() + // Write the key into the hash. + d.Write(k) + // Now write the data. + d.Write(buf) + // Read 32 bytes of output from the hash into h. + d.Read(h) +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/shake.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/shake.go new file mode 100644 index 0000000..841f986 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/shake.go @@ -0,0 +1,60 @@ +// Copyright 2014 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 sha3 + +// This file defines the ShakeHash interface, and provides +// functions for creating SHAKE instances, as well as utility +// functions for hashing bytes to arbitrary-length output. + +import ( + "io" +) + +// ShakeHash defines the interface to hash functions that +// support arbitrary-length output. +type ShakeHash interface { + // Write absorbs more data into the hash's state. It panics if input is + // written to it after output has been read from it. + io.Writer + + // Read reads more output from the hash; reading affects the hash's + // state. (ShakeHash.Read is thus very different from Hash.Sum) + // It never returns an error. + io.Reader + + // Clone returns a copy of the ShakeHash in its current state. + Clone() ShakeHash + + // Reset resets the ShakeHash to its initial state. + Reset() +} + +func (d *state) Clone() ShakeHash { + return d.clone() +} + +// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 128 bits against all attacks if at +// least 32 bytes of its output are used. +func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} } + +// NewShake256 creates a new SHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 256 bits against all attacks if +// at least 64 bytes of its output are used. +func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} } + +// ShakeSum128 writes an arbitrary-length digest of data into hash. +func ShakeSum128(hash, data []byte) { + h := NewShake128() + h.Write(data) + h.Read(hash) +} + +// ShakeSum256 writes an arbitrary-length digest of data into hash. +func ShakeSum256(hash, data []byte) { + h := NewShake256() + h.Write(data) + h.Read(hash) +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/testdata/keccakKats.json.deflate b/Godeps/_workspace/src/golang.org/x/crypto/sha3/testdata/keccakKats.json.deflate new file mode 100644 index 0000000..62e85ae Binary files /dev/null and b/Godeps/_workspace/src/golang.org/x/crypto/sha3/testdata/keccakKats.json.deflate differ diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor.go new file mode 100644 index 0000000..d622979 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor.go @@ -0,0 +1,16 @@ +// Copyright 2015 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. + +// +build !amd64,!386 appengine + +package sha3 + +var ( + xorIn = xorInGeneric + copyOut = copyOutGeneric + xorInUnaligned = xorInGeneric + copyOutUnaligned = copyOutGeneric +) + +const xorImplementationUnaligned = "generic" diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_generic.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_generic.go new file mode 100644 index 0000000..fd35f02 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_generic.go @@ -0,0 +1,28 @@ +// Copyright 2015 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 sha3 + +import "encoding/binary" + +// xorInGeneric xors the bytes in buf into the state; it +// makes no non-portable assumptions about memory layout +// or alignment. +func xorInGeneric(d *state, buf []byte) { + n := len(buf) / 8 + + for i := 0; i < n; i++ { + a := binary.LittleEndian.Uint64(buf) + d.a[i] ^= a + buf = buf[8:] + } +} + +// copyOutGeneric copies ulint64s to a byte buffer. +func copyOutGeneric(d *state, b []byte) { + for i := 0; len(b) >= 8; i++ { + binary.LittleEndian.PutUint64(b, d.a[i]) + b = b[8:] + } +} diff --git a/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_unaligned.go b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_unaligned.go new file mode 100644 index 0000000..c7851a1 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/crypto/sha3/xor_unaligned.go @@ -0,0 +1,58 @@ +// Copyright 2015 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. + +// +build amd64 386 +// +build !appengine + +package sha3 + +import "unsafe" + +func xorInUnaligned(d *state, buf []byte) { + bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0])) + n := len(buf) + if n >= 72 { + d.a[0] ^= bw[0] + d.a[1] ^= bw[1] + d.a[2] ^= bw[2] + d.a[3] ^= bw[3] + d.a[4] ^= bw[4] + d.a[5] ^= bw[5] + d.a[6] ^= bw[6] + d.a[7] ^= bw[7] + d.a[8] ^= bw[8] + } + if n >= 104 { + d.a[9] ^= bw[9] + d.a[10] ^= bw[10] + d.a[11] ^= bw[11] + d.a[12] ^= bw[12] + } + if n >= 136 { + d.a[13] ^= bw[13] + d.a[14] ^= bw[14] + d.a[15] ^= bw[15] + d.a[16] ^= bw[16] + } + if n >= 144 { + d.a[17] ^= bw[17] + } + if n >= 168 { + d.a[18] ^= bw[18] + d.a[19] ^= bw[19] + d.a[20] ^= bw[20] + } +} + +func copyOutUnaligned(d *state, buf []byte) { + ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) + copy(buf, ab[:]) +} + +var ( + xorIn = xorInUnaligned + copyOut = copyOutUnaligned +) + +const xorImplementationUnaligned = "unaligned" diff --git a/convert.go b/convert.go index ac05f2e..944c788 100644 --- a/convert.go +++ b/convert.go @@ -57,7 +57,7 @@ func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { return ipm.Encapsulate(udpm), nil case "utp", "utp4", "utp6": - acc, ok := a.(*utp.UTPAddr) + acc, ok := a.(*utp.Addr) if !ok { return nil, errIncorrectNetAddr } @@ -117,7 +117,7 @@ func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) { case "udp", "udp4", "udp6": return net.ResolveUDPAddr(network, host) case "utp", "utp4", "utp6": - return utp.ResolveUTPAddr(network, host) + return utp.ResolveAddr(network, host) case "ip", "ip4", "ip6": return net.ResolveIPAddr(network, host) } diff --git a/convert_test.go b/convert_test.go index f9aaac3..ebdcb97 100644 --- a/convert_test.go +++ b/convert_test.go @@ -91,7 +91,7 @@ func TestFromUDP(t *testing.T) { func TestFromUTP(t *testing.T) { testConvert(t, "/ip4/10.20.30.40/udp/1234/utp", func() (ma.Multiaddr, error) { - return FromNetAddr(&utp.UTPAddr{ + return FromNetAddr(&utp.Addr{ Addr: &net.UDPAddr{ IP: net.ParseIP("10.20.30.40"), Port: 1234,