From c5510ff4b81890ec6e3fecbe8097432fd73d2980 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 3 Jul 2014 23:42:24 -0700 Subject: [PATCH] string <> []byte --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++ codec.go | 50 ++++++++++++++++++++++++++++++++ convert.go | 43 +++++++++++++++++++++++++++ index.go | 10 +++++++ multiaddr_test.go | 51 ++++++++++++++++++++++++++++++++ protocols.go | 42 +++++++++++++++++++++++++++ 6 files changed, 270 insertions(+) create mode 100644 README.md create mode 100644 codec.go create mode 100644 convert.go create mode 100644 index.go create mode 100644 multiaddr_test.go create mode 100644 protocols.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..06f3e04 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# go-multiaddr + +[multiaddr](https://github.com/jbenet/multiaddr) implementation in Go. + +## Example + +### Simple + +```go +import "github.com/jbenet/go-multiaddr" + +m := multiaddr.NewString("/ip4/127.0.0.1/udp/1234") +// +m.buffer +// +m.String() +// /ip4/127.0.0.1/udp/1234 + +// construct with Buffer +m = multiaddr.Multiaddr{ Bytes: m.Bytes } +// +``` + +### Protocols + +```go +// get the multiaddr protocol codes +m.ProtoCodes() +// []int{4, 6} + +// get the multiaddr protocol string codes +m.ProtoNames() +// []string{"ip4", "tcp"} + +// get the multiaddr protocol description objects +addr.Protos() +// []Protocol{ +// Protocol{ Code: 4, Name: 'ip4', Size: 32}, +// Protocol{ Code: 17, Name: 'udp', Size: 16}, +// } +``` + +### Other formats + +```go +// handles the stupid url version too +m = multiaddr.NewUrl("udp4://127.0.0.1:1234") +// +m.Url(buf) +// udp4://127.0.0.1:1234 +``` + +### En/decapsulate + +```go +m.Encapsulate(m.NewString("/sctp/5678")) +// +m.Decapsulate(m.NewString("/udp")) // up to + inc last occurrence of subaddr +// +``` + +### Tunneling + +Multiaddr allows expressing tunnels very nicely. + +```js +printer := multiaddr.NewString("/ip4/192.168.0.13/tcp/80") +proxy := multiaddr.NewString("/ip4/10.20.30.40/tcp/443") +printerOverProxy := proxy.Encapsulate(printer) +// + +proxyAgain := printerOverProxy.Decapsulate(multiaddr.NewString("/ip4")) +// +``` diff --git a/codec.go b/codec.go new file mode 100644 index 0000000..48b1de0 --- /dev/null +++ b/codec.go @@ -0,0 +1,50 @@ +package multiaddr + +import( + "fmt" + "strings" +) + + +func StringToBytes(s string) ([]byte, error) { + b := []byte{} + sp := strings.Split(s, "/") + + // consume first empty elem + sp = sp[1:] + + for ; len(sp) > 0 ; { + p := ProtocolWithName(sp[0]) + if p == nil { + return nil, fmt.Errorf("no protocol with name %s", sp[0]) + } + b = append(b, byte(p.Code)) + + a := AddressStringToBytes(p, sp[1]) + b = append(b, a...) + + sp = sp[2:] + } + return b, nil +} + +func BytesToString(b []byte) (string, error) { + s := "" + + for ; len(b) > 0 ; { + p := ProtocolWithCode(int(b[0])) + if p == nil { + return "", fmt.Errorf("no protocol with code %d", b[0]) + } + s = strings.Join([]string{s, "/", p.Name}, "") + b = b[1:] + + a := AddressBytesToString(p, b) + if len(a) > 0 { + s = strings.Join([]string{s, "/", a}, "") + } + b = b[(p.Size / 8):] + } + + return s, nil +} diff --git a/convert.go b/convert.go new file mode 100644 index 0000000..dc988f3 --- /dev/null +++ b/convert.go @@ -0,0 +1,43 @@ +package multiaddr + +import ( + "encoding/binary" + "net" + "strconv" +) + +func AddressStringToBytes(p *Protocol, s string) []byte { + switch p.Code { + + // ipv4,6 + case 4, 41: + return net.ParseIP(s).To4() + + // tcp udp dccp sctp + case 6, 17, 33, 132: + b := make([]byte, 2) + i, err := strconv.Atoi(s) + if err == nil { + binary.BigEndian.PutUint16(b, uint16(i)) + } + return b + } + + return []byte{} +} + +func AddressBytesToString(p *Protocol, b []byte) string { + switch p.Code { + + // ipv4,6 + case 4, 41: + return net.IP(b).String() + + // tcp udp dccp sctp + case 6, 17, 33, 132: + i := binary.BigEndian.Uint16(b) + return strconv.Itoa(int(i)) + } + + return "" +} diff --git a/index.go b/index.go new file mode 100644 index 0000000..aaef5d3 --- /dev/null +++ b/index.go @@ -0,0 +1,10 @@ +package multiaddr + +type Multiaddr struct { + Bytes []byte +} + +func NewString(s string) *Multiaddr { + m := &Multiaddr{} + return m +} diff --git a/multiaddr_test.go b/multiaddr_test.go new file mode 100644 index 0000000..e14f79c --- /dev/null +++ b/multiaddr_test.go @@ -0,0 +1,51 @@ +package multiaddr + +import ( + "bytes" + "testing" + "encoding/hex" +) + + +func TestStringToBytes(t *testing.T) { + + testString := func(s string, h string) { + b1, err := hex.DecodeString(h) + if err != nil { + t.Error("failed to decode hex", h) + } + + b2, err := StringToBytes(s) + if err != nil { + t.Error("failed to convert", s) + } + + if !bytes.Equal(b1, b2) { + t.Error("failed to convert", s, "to", b1, "got", b2) + } + } + + testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2") +} + +func TestBytesToString(t *testing.T) { + + testString := func(s1 string, h string) { + b, err := hex.DecodeString(h) + if err != nil { + t.Error("failed to decode hex", h) + } + + s2, err := BytesToString(b) + if err != nil { + t.Error("failed to convert", b) + } + + if s1 == s2 { + t.Error("failed to convert", b, "to", s1, "got", s2) + } + } + + testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2") +} + diff --git a/protocols.go b/protocols.go new file mode 100644 index 0000000..fac7344 --- /dev/null +++ b/protocols.go @@ -0,0 +1,42 @@ +package multiaddr + +type Protocol struct { + Code int + Size int + Name string +} + +// replicating table here to: +// 1. avoid parsing the csv +// 2. ensuring errors in the csv don't screw up code. +// 3. changing a number has to happen in two places. + +var Protocols = []Protocol{ + Protocol{4, 32, "ip4"}, + Protocol{6, 16, "tcp"}, + Protocol{17, 16, "udp"}, + Protocol{33, 16, "dccp"}, + Protocol{41, 128, "ip6"}, + // these require varint: + Protocol{132, 16, "sctp"}, + // {480, 0, "http"}, + // {443, 0, "https"}, +} + +func ProtocolWithName(s string) *Protocol { + for _, p := range(Protocols) { + if p.Name == s { + return &p + } + } + return nil +} + +func ProtocolWithCode(c int) *Protocol { + for _, p := range(Protocols) { + if p.Code == c { + return &p + } + } + return nil +}