forked from cerc-io/ipld-eth-server
update dependencies to work with update eth-block-extractor
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
// Copyright 2011 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 base32 implements base32 encoding as specified by RFC 4648.
|
||||
package base32
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
/*
|
||||
* Encodings
|
||||
*/
|
||||
|
||||
// An Encoding is a radix 32 encoding/decoding scheme, defined by a
|
||||
// 32-character alphabet. The most common is the "base32" encoding
|
||||
// introduced for SASL GSSAPI and standardized in RFC 4648.
|
||||
// The alternate "base32hex" encoding is used in DNSSEC.
|
||||
type Encoding struct {
|
||||
encode string
|
||||
decodeMap [256]byte
|
||||
padChar rune
|
||||
}
|
||||
|
||||
// Alphabet returns the Base32 alphabet used
|
||||
func (enc *Encoding) Alphabet() string {
|
||||
return enc.encode
|
||||
}
|
||||
|
||||
const (
|
||||
StdPadding rune = '='
|
||||
NoPadding rune = -1
|
||||
)
|
||||
|
||||
const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
const encodeHex = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
|
||||
|
||||
// NewEncoding returns a new Encoding defined by the given alphabet,
|
||||
// which must be a 32-byte string.
|
||||
func NewEncoding(encoder string) *Encoding {
|
||||
e := new(Encoding)
|
||||
e.padChar = StdPadding
|
||||
e.encode = encoder
|
||||
for i := 0; i < len(e.decodeMap); i++ {
|
||||
e.decodeMap[i] = 0xFF
|
||||
}
|
||||
for i := 0; i < len(encoder); i++ {
|
||||
e.decodeMap[encoder[i]] = byte(i)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// NewEncoding returns a new case insensitive Encoding defined by the
|
||||
// given alphabet, which must be a 32-byte string.
|
||||
func NewEncodingCI(encoder string) *Encoding {
|
||||
e := new(Encoding)
|
||||
e.padChar = StdPadding
|
||||
e.encode = encoder
|
||||
for i := 0; i < len(e.decodeMap); i++ {
|
||||
e.decodeMap[i] = 0xFF
|
||||
}
|
||||
for i := 0; i < len(encoder); i++ {
|
||||
e.decodeMap[asciiToLower(encoder[i])] = byte(i)
|
||||
e.decodeMap[asciiToUpper(encoder[i])] = byte(i)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func asciiToLower(c byte) byte {
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c + 32
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func asciiToUpper(c byte) byte {
|
||||
if c >= 'a' && c <= 'z' {
|
||||
return c - 32
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// WithPadding creates a new encoding identical to enc except
|
||||
// with a specified padding character, or NoPadding to disable padding.
|
||||
func (enc Encoding) WithPadding(padding rune) *Encoding {
|
||||
enc.padChar = padding
|
||||
return &enc
|
||||
}
|
||||
|
||||
// StdEncoding is the standard base32 encoding, as defined in
|
||||
// RFC 4648.
|
||||
var StdEncoding = NewEncodingCI(encodeStd)
|
||||
|
||||
// HexEncoding is the ``Extended Hex Alphabet'' defined in RFC 4648.
|
||||
// It is typically used in DNS.
|
||||
var HexEncoding = NewEncodingCI(encodeHex)
|
||||
|
||||
var RawStdEncoding = NewEncodingCI(encodeStd).WithPadding(NoPadding)
|
||||
var RawHexEncoding = NewEncodingCI(encodeHex).WithPadding(NoPadding)
|
||||
|
||||
/*
|
||||
* Encoder
|
||||
*/
|
||||
|
||||
// Encode encodes src using the encoding enc, writing
|
||||
// EncodedLen(len(src)) bytes to dst.
|
||||
//
|
||||
// The encoding pads the output to a multiple of 8 bytes,
|
||||
// so Encode is not appropriate for use on individual blocks
|
||||
// of a large data stream. Use NewEncoder() instead.
|
||||
func (enc *Encoding) Encode(dst, src []byte) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
var carry byte
|
||||
|
||||
// Unpack 8x 5-bit source blocks into a 5 byte
|
||||
// destination quantum
|
||||
switch len(src) {
|
||||
default:
|
||||
dst[7] = enc.encode[src[4]&0x1F]
|
||||
carry = src[4] >> 5
|
||||
fallthrough
|
||||
case 4:
|
||||
dst[6] = enc.encode[carry|(src[3]<<3)&0x1F]
|
||||
dst[5] = enc.encode[(src[3]>>2)&0x1F]
|
||||
carry = src[3] >> 7
|
||||
fallthrough
|
||||
case 3:
|
||||
dst[4] = enc.encode[carry|(src[2]<<1)&0x1F]
|
||||
carry = (src[2] >> 4) & 0x1F
|
||||
fallthrough
|
||||
case 2:
|
||||
dst[3] = enc.encode[carry|(src[1]<<4)&0x1F]
|
||||
dst[2] = enc.encode[(src[1]>>1)&0x1F]
|
||||
carry = (src[1] >> 6) & 0x1F
|
||||
fallthrough
|
||||
case 1:
|
||||
dst[1] = enc.encode[carry|(src[0]<<2)&0x1F]
|
||||
dst[0] = enc.encode[src[0]>>3]
|
||||
}
|
||||
|
||||
// Pad the final quantum
|
||||
if len(src) < 5 {
|
||||
if enc.padChar != NoPadding {
|
||||
dst[7] = byte(enc.padChar)
|
||||
if len(src) < 4 {
|
||||
dst[6] = byte(enc.padChar)
|
||||
dst[5] = byte(enc.padChar)
|
||||
if len(src) < 3 {
|
||||
dst[4] = byte(enc.padChar)
|
||||
if len(src) < 2 {
|
||||
dst[3] = byte(enc.padChar)
|
||||
dst[2] = byte(enc.padChar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
src = src[5:]
|
||||
dst = dst[8:]
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeToString returns the base32 encoding of src.
|
||||
func (enc *Encoding) EncodeToString(src []byte) string {
|
||||
buf := make([]byte, enc.EncodedLen(len(src)))
|
||||
enc.Encode(buf, src)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
err error
|
||||
enc *Encoding
|
||||
w io.Writer
|
||||
buf [5]byte // buffered data waiting to be encoded
|
||||
nbuf int // number of bytes in buf
|
||||
out [1024]byte // output buffer
|
||||
}
|
||||
|
||||
func (e *encoder) Write(p []byte) (n int, err error) {
|
||||
if e.err != nil {
|
||||
return 0, e.err
|
||||
}
|
||||
|
||||
// Leading fringe.
|
||||
if e.nbuf > 0 {
|
||||
var i int
|
||||
for i = 0; i < len(p) && e.nbuf < 5; i++ {
|
||||
e.buf[e.nbuf] = p[i]
|
||||
e.nbuf++
|
||||
}
|
||||
n += i
|
||||
p = p[i:]
|
||||
if e.nbuf < 5 {
|
||||
return
|
||||
}
|
||||
e.enc.Encode(e.out[0:], e.buf[0:])
|
||||
if _, e.err = e.w.Write(e.out[0:8]); e.err != nil {
|
||||
return n, e.err
|
||||
}
|
||||
e.nbuf = 0
|
||||
}
|
||||
|
||||
// Large interior chunks.
|
||||
for len(p) >= 5 {
|
||||
nn := len(e.out) / 8 * 5
|
||||
if nn > len(p) {
|
||||
nn = len(p)
|
||||
nn -= nn % 5
|
||||
}
|
||||
e.enc.Encode(e.out[0:], p[0:nn])
|
||||
if _, e.err = e.w.Write(e.out[0 : nn/5*8]); e.err != nil {
|
||||
return n, e.err
|
||||
}
|
||||
n += nn
|
||||
p = p[nn:]
|
||||
}
|
||||
|
||||
// Trailing fringe.
|
||||
for i := 0; i < len(p); i++ {
|
||||
e.buf[i] = p[i]
|
||||
}
|
||||
e.nbuf = len(p)
|
||||
n += len(p)
|
||||
return
|
||||
}
|
||||
|
||||
// Close flushes any pending output from the encoder.
|
||||
// It is an error to call Write after calling Close.
|
||||
func (e *encoder) Close() error {
|
||||
// If there's anything left in the buffer, flush it out
|
||||
if e.err == nil && e.nbuf > 0 {
|
||||
e.enc.Encode(e.out[0:], e.buf[0:e.nbuf])
|
||||
e.nbuf = 0
|
||||
_, e.err = e.w.Write(e.out[0:8])
|
||||
}
|
||||
return e.err
|
||||
}
|
||||
|
||||
// NewEncoder returns a new base32 stream encoder. Data written to
|
||||
// the returned writer will be encoded using enc and then written to w.
|
||||
// Base32 encodings operate in 5-byte blocks; when finished
|
||||
// writing, the caller must Close the returned encoder to flush any
|
||||
// partially written blocks.
|
||||
func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser {
|
||||
return &encoder{enc: enc, w: w}
|
||||
}
|
||||
|
||||
// EncodedLen returns the length in bytes of the base32 encoding
|
||||
// of an input buffer of length n.
|
||||
func (enc *Encoding) EncodedLen(n int) int {
|
||||
if enc.padChar == NoPadding {
|
||||
return (n*8 + 4) / 5 // minimum # chars at 5 bits per char
|
||||
}
|
||||
return (n + 4) / 5 * 8
|
||||
}
|
||||
|
||||
/*
|
||||
* Decoder
|
||||
*/
|
||||
|
||||
type CorruptInputError int64
|
||||
|
||||
func (e CorruptInputError) Error() string {
|
||||
return "illegal base32 data at input byte " + strconv.FormatInt(int64(e), 10)
|
||||
}
|
||||
|
||||
// decode is like Decode but returns an additional 'end' value, which
|
||||
// indicates if end-of-message padding was encountered and thus any
|
||||
// additional data is an error. This method assumes that src has been
|
||||
// stripped of all supported whitespace ('\r' and '\n').
|
||||
func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
|
||||
olen := len(src)
|
||||
for len(src) > 0 && !end {
|
||||
// Decode quantum using the base32 alphabet
|
||||
var dbuf [8]byte
|
||||
dlen := 8
|
||||
|
||||
for j := 0; j < 8; {
|
||||
if len(src) == 0 {
|
||||
if enc.padChar != NoPadding {
|
||||
return n, false, CorruptInputError(olen - len(src) - j)
|
||||
}
|
||||
dlen = j
|
||||
break
|
||||
}
|
||||
in := src[0]
|
||||
src = src[1:]
|
||||
if in == byte(enc.padChar) && j >= 2 && len(src) < 8 {
|
||||
if enc.padChar == NoPadding {
|
||||
return n, false, CorruptInputError(olen)
|
||||
}
|
||||
|
||||
// We've reached the end and there's padding
|
||||
if len(src)+j < 8-1 {
|
||||
// not enough padding
|
||||
return n, false, CorruptInputError(olen)
|
||||
}
|
||||
for k := 0; k < 8-1-j; k++ {
|
||||
if len(src) > k && src[k] != byte(enc.padChar) {
|
||||
// incorrect padding
|
||||
return n, false, CorruptInputError(olen - len(src) + k - 1)
|
||||
}
|
||||
}
|
||||
dlen, end = j, true
|
||||
// 7, 5 and 2 are not valid padding lengths, and so 1, 3 and 6 are not
|
||||
// valid dlen values. See RFC 4648 Section 6 "Base 32 Encoding" listing
|
||||
// the five valid padding lengths, and Section 9 "Illustrations and
|
||||
// Examples" for an illustration for how the 1st, 3rd and 6th base32
|
||||
// src bytes do not yield enough information to decode a dst byte.
|
||||
if dlen == 1 || dlen == 3 || dlen == 6 {
|
||||
return n, false, CorruptInputError(olen - len(src) - 1)
|
||||
}
|
||||
break
|
||||
}
|
||||
dbuf[j] = enc.decodeMap[in]
|
||||
if dbuf[j] == 0xFF {
|
||||
return n, false, CorruptInputError(olen - len(src) - 1)
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
// Pack 8x 5-bit source blocks into 5 byte destination
|
||||
// quantum
|
||||
switch dlen {
|
||||
case 8:
|
||||
dst[4] = dbuf[6]<<5 | dbuf[7]
|
||||
fallthrough
|
||||
case 7:
|
||||
dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3
|
||||
fallthrough
|
||||
case 5:
|
||||
dst[2] = dbuf[3]<<4 | dbuf[4]>>1
|
||||
fallthrough
|
||||
case 4:
|
||||
dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4
|
||||
fallthrough
|
||||
case 2:
|
||||
dst[0] = dbuf[0]<<3 | dbuf[1]>>2
|
||||
}
|
||||
|
||||
if len(dst) > 5 {
|
||||
dst = dst[5:]
|
||||
}
|
||||
|
||||
switch dlen {
|
||||
case 2:
|
||||
n += 1
|
||||
case 4:
|
||||
n += 2
|
||||
case 5:
|
||||
n += 3
|
||||
case 7:
|
||||
n += 4
|
||||
case 8:
|
||||
n += 5
|
||||
}
|
||||
}
|
||||
return n, end, nil
|
||||
}
|
||||
|
||||
// Decode decodes src using the encoding enc. It writes at most
|
||||
// DecodedLen(len(src)) bytes to dst and returns the number of bytes
|
||||
// written. If src contains invalid base32 data, it will return the
|
||||
// number of bytes successfully written and CorruptInputError.
|
||||
// New line characters (\r and \n) are ignored.
|
||||
func (enc *Encoding) Decode(dst, s []byte) (n int, err error) {
|
||||
// FIXME: if dst is the same as s use decodeInPlace
|
||||
stripped := make([]byte, 0, len(s))
|
||||
for _, c := range s {
|
||||
if c != '\r' && c != '\n' {
|
||||
stripped = append(stripped, c)
|
||||
}
|
||||
}
|
||||
n, _, err = enc.decode(dst, stripped)
|
||||
return
|
||||
}
|
||||
|
||||
func (enc *Encoding) decodeInPlace(strb []byte) (n int, err error) {
|
||||
off := 0
|
||||
for _, b := range strb {
|
||||
if b == '\n' || b == '\r' {
|
||||
continue
|
||||
}
|
||||
strb[off] = b
|
||||
off++
|
||||
}
|
||||
n, _, err = enc.decode(strb, strb[:off])
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeString returns the bytes represented by the base32 string s.
|
||||
func (enc *Encoding) DecodeString(s string) ([]byte, error) {
|
||||
strb := []byte(s)
|
||||
n, err := enc.decodeInPlace(strb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strb[:n], nil
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
err error
|
||||
enc *Encoding
|
||||
r io.Reader
|
||||
end bool // saw end of message
|
||||
buf [1024]byte // leftover input
|
||||
nbuf int
|
||||
out []byte // leftover decoded output
|
||||
outbuf [1024 / 8 * 5]byte
|
||||
}
|
||||
|
||||
func (d *decoder) Read(p []byte) (n int, err error) {
|
||||
if d.err != nil {
|
||||
return 0, d.err
|
||||
}
|
||||
|
||||
// Use leftover decoded output from last read.
|
||||
if len(d.out) > 0 {
|
||||
n = copy(p, d.out)
|
||||
d.out = d.out[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read a chunk.
|
||||
nn := len(p) / 5 * 8
|
||||
if nn < 8 {
|
||||
nn = 8
|
||||
}
|
||||
if nn > len(d.buf) {
|
||||
nn = len(d.buf)
|
||||
}
|
||||
nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 8-d.nbuf)
|
||||
d.nbuf += nn
|
||||
if d.nbuf < 8 {
|
||||
return 0, d.err
|
||||
}
|
||||
|
||||
// Decode chunk into p, or d.out and then p if p is too small.
|
||||
nr := d.nbuf / 8 * 8
|
||||
nw := d.nbuf / 8 * 5
|
||||
if nw > len(p) {
|
||||
nw, d.end, d.err = d.enc.decode(d.outbuf[0:], d.buf[0:nr])
|
||||
d.out = d.outbuf[0:nw]
|
||||
n = copy(p, d.out)
|
||||
d.out = d.out[n:]
|
||||
} else {
|
||||
n, d.end, d.err = d.enc.decode(p, d.buf[0:nr])
|
||||
}
|
||||
d.nbuf -= nr
|
||||
for i := 0; i < d.nbuf; i++ {
|
||||
d.buf[i] = d.buf[i+nr]
|
||||
}
|
||||
|
||||
if d.err == nil {
|
||||
d.err = err
|
||||
}
|
||||
return n, d.err
|
||||
}
|
||||
|
||||
type newlineFilteringReader struct {
|
||||
wrapped io.Reader
|
||||
}
|
||||
|
||||
func (r *newlineFilteringReader) Read(p []byte) (int, error) {
|
||||
n, err := r.wrapped.Read(p)
|
||||
for n > 0 {
|
||||
offset := 0
|
||||
for i, b := range p[0:n] {
|
||||
if b != '\r' && b != '\n' {
|
||||
if i != offset {
|
||||
p[offset] = b
|
||||
}
|
||||
offset++
|
||||
}
|
||||
}
|
||||
if offset > 0 {
|
||||
return offset, err
|
||||
}
|
||||
// Previous buffer entirely whitespace, read again
|
||||
n, err = r.wrapped.Read(p)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// NewDecoder constructs a new base32 stream decoder.
|
||||
func NewDecoder(enc *Encoding, r io.Reader) io.Reader {
|
||||
return &decoder{enc: enc, r: &newlineFilteringReader{r}}
|
||||
}
|
||||
|
||||
// DecodedLen returns the maximum length in bytes of the decoded data
|
||||
// corresponding to n bytes of base32-encoded data.
|
||||
func (enc *Encoding) DecodedLen(n int) int {
|
||||
if enc.padChar == NoPadding {
|
||||
return (n*5 + 7) / 8
|
||||
}
|
||||
|
||||
return n / 8 * 5
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/multiformats/go-base32
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"author": "Golang",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-base32"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-base32"
|
||||
},
|
||||
"gxVersion": "0.7.0",
|
||||
"language": "go",
|
||||
"license": "BSD-3",
|
||||
"name": "base32",
|
||||
"version": "0.0.3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/madns/madns
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -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.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# go-multiaddr-dns
|
||||
|
||||
> Resolve /dns4, /dns6, and /dnsaddr multiaddrs.
|
||||
|
||||
```sh
|
||||
> madns /dnsaddr/ipfs.io/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
/ip6/2604:a880:1:20::1d9:6001/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
/ip6/fc3d:9a4e:3c96:2fd2:1afa:18fe:8dd2:b602/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
/dns4/jupiter.i.ipfs.io/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
/dns6/jupiter.i.ipfs.io/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
|
||||
```
|
||||
|
||||
|
||||
In more detail:
|
||||
|
||||
```sh
|
||||
> madns /dns6/example.net
|
||||
/ip6/2001:db8::a3
|
||||
/ip6/2001:db8::a4
|
||||
...
|
||||
|
||||
> madns /dns4/example.net/tcp/443/wss
|
||||
/ip4/192.0.2.1/tcp/443/wss
|
||||
/ip4/192.0.2.2/tcp/443/wss
|
||||
|
||||
# No-op if it's not a dns-ish address.
|
||||
|
||||
> madns /ip4/127.0.0.1/tcp/8080
|
||||
/ip4/127.0.0.1/tcp/8080
|
||||
|
||||
# /dnsaddr resolves by looking up TXT records.
|
||||
|
||||
> dig +short TXT _dnsaddr.example.net
|
||||
"dnsaddr=/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo"
|
||||
"dnsaddr=/ip6/2001:db8::a4/tcp/443/wss/ipfs/Qmbar"
|
||||
"dnsaddr=/ip4/192.0.2.1/tcp/443/wss/ipfs/Qmfoo"
|
||||
"dnsaddr=/ip4/192.0.2.2/tcp/443/wss/ipfs/Qmbar"
|
||||
...
|
||||
|
||||
# /dnsaddr returns addrs which encapsulate whatever /dnsaddr encapsulates too.
|
||||
|
||||
> madns example.net/ipfs/Qmfoo
|
||||
info: changing query to /dnsaddr/example.net/ipfs/Qmfoo
|
||||
/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo
|
||||
/ip4/192.0.2.1/tcp/443/wss/ipfs/Qmfoo
|
||||
|
||||
# TODO -p filters by protocol stacks.
|
||||
|
||||
> madns -p /ip6/tcp/wss /dnsaddr/example.net
|
||||
/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo
|
||||
/ip6/2001:db8::a4/tcp/443/wss/ipfs/Qmbar
|
||||
|
||||
# TOOD -c filters by CIDR
|
||||
> madns -c /ip4/104.236.76.0/ipcidr/24 /dnsaddr/example.net
|
||||
/ip4/192.0.2.2/tcp/443/wss/ipfs/Qmbar
|
||||
```
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package madns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// Extracted from source of truth for multicodec codes: https://github.com/multiformats/multicodec
|
||||
const (
|
||||
P_DNS4 = 0x0036
|
||||
P_DNS6 = 0x0037
|
||||
P_DNSADDR = 0x0038
|
||||
)
|
||||
|
||||
var Dns4Protocol = ma.Protocol{
|
||||
Code: P_DNS4,
|
||||
Size: ma.LengthPrefixedVarSize,
|
||||
Name: "dns4",
|
||||
VCode: ma.CodeToVarint(P_DNS4),
|
||||
Transcoder: DnsTranscoder,
|
||||
}
|
||||
var Dns6Protocol = ma.Protocol{
|
||||
Code: P_DNS6,
|
||||
Size: ma.LengthPrefixedVarSize,
|
||||
Name: "dns6",
|
||||
VCode: ma.CodeToVarint(P_DNS6),
|
||||
Transcoder: DnsTranscoder,
|
||||
}
|
||||
var DnsaddrProtocol = ma.Protocol{
|
||||
Code: P_DNSADDR,
|
||||
Size: ma.LengthPrefixedVarSize,
|
||||
Name: "dnsaddr",
|
||||
VCode: ma.CodeToVarint(P_DNSADDR),
|
||||
Transcoder: DnsTranscoder,
|
||||
}
|
||||
|
||||
func init() {
|
||||
err := ma.AddProtocol(Dns4Protocol)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error registering dns4 protocol: %s", err))
|
||||
}
|
||||
err = ma.AddProtocol(Dns6Protocol)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error registering dns6 protocol: %s", err))
|
||||
}
|
||||
err = ma.AddProtocol(DnsaddrProtocol)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error registering dnsaddr protocol: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
var DnsTranscoder = ma.NewTranscoderFromFunctions(dnsStB, dnsBtS, dnsVal)
|
||||
|
||||
func dnsVal(b []byte) error {
|
||||
if bytes.IndexByte(b, '/') >= 0 {
|
||||
return fmt.Errorf("domain name %q contains a slash", string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dnsStB(s string) ([]byte, error) {
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
func dnsBtS(b []byte) (string, error) {
|
||||
return string(b), nil
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/multiformats/go-multiaddr-dns
|
||||
|
||||
require github.com/multiformats/go-multiaddr v0.0.1
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-multiaddr v0.0.1 h1:/QUV3VBMDI6pi6xfiw7lr6xhDWWvQKn9udPn68kLSdY=
|
||||
github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
|
||||
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
|
||||
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"author": "lgierth",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multiaddr-dns/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multiaddr-dns"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "multiformats",
|
||||
"hash": "QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL",
|
||||
"name": "go-multiaddr",
|
||||
"version": "1.4.1"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.10.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "go-multiaddr-dns",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "0.3.2"
|
||||
}
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package madns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var ResolvableProtocols = []ma.Protocol{DnsaddrProtocol, Dns4Protocol, Dns6Protocol}
|
||||
var DefaultResolver = &Resolver{Backend: net.DefaultResolver}
|
||||
|
||||
type backend interface {
|
||||
LookupIPAddr(context.Context, string) ([]net.IPAddr, error)
|
||||
LookupTXT(context.Context, string) ([]string, error)
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
Backend backend
|
||||
}
|
||||
|
||||
type MockBackend struct {
|
||||
IP map[string][]net.IPAddr
|
||||
TXT map[string][]string
|
||||
}
|
||||
|
||||
func (r *MockBackend) LookupIPAddr(ctx context.Context, name string) ([]net.IPAddr, error) {
|
||||
results, ok := r.IP[name]
|
||||
if ok {
|
||||
return results, nil
|
||||
} else {
|
||||
return []net.IPAddr{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *MockBackend) LookupTXT(ctx context.Context, name string) ([]string, error) {
|
||||
results, ok := r.TXT[name]
|
||||
if ok {
|
||||
return results, nil
|
||||
} else {
|
||||
return []string{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Matches(maddr ma.Multiaddr) bool {
|
||||
protos := maddr.Protocols()
|
||||
if len(protos) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, p := range ResolvableProtocols {
|
||||
if protos[0].Code == p.Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||
return DefaultResolver.Resolve(ctx, maddr)
|
||||
}
|
||||
|
||||
func (r *Resolver) Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||
if !Matches(maddr) {
|
||||
return []ma.Multiaddr{maddr}, nil
|
||||
}
|
||||
|
||||
protos := maddr.Protocols()
|
||||
if protos[0].Code == Dns4Protocol.Code {
|
||||
return r.resolveDns4(ctx, maddr)
|
||||
}
|
||||
if protos[0].Code == Dns6Protocol.Code {
|
||||
return r.resolveDns6(ctx, maddr)
|
||||
}
|
||||
if protos[0].Code == DnsaddrProtocol.Code {
|
||||
return r.resolveDnsaddr(ctx, maddr)
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveDns4(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||
value, err := maddr.ValueForProtocol(Dns4Protocol.Code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s: %s", maddr.String(), err)
|
||||
}
|
||||
|
||||
encap := ma.Split(maddr)[1:]
|
||||
|
||||
result := []ma.Multiaddr{}
|
||||
records, err := r.Backend.LookupIPAddr(ctx, value)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
ip4 := r.IP.To4()
|
||||
if ip4 == nil {
|
||||
continue
|
||||
}
|
||||
ip4maddr, err := ma.NewMultiaddr("/ip4/" + ip4.String())
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
parts := append([]ma.Multiaddr{ip4maddr}, encap...)
|
||||
result = append(result, ma.Join(parts...))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveDns6(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||
value, err := maddr.ValueForProtocol(Dns6Protocol.Code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s: %s", maddr.String(), err)
|
||||
}
|
||||
|
||||
encap := ma.Split(maddr)[1:]
|
||||
|
||||
result := []ma.Multiaddr{}
|
||||
records, err := r.Backend.LookupIPAddr(ctx, value)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
if r.IP.To4() != nil {
|
||||
continue
|
||||
}
|
||||
ip6maddr, err := ma.NewMultiaddr("/ip6/" + r.IP.To16().String())
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
parts := append([]ma.Multiaddr{ip6maddr}, encap...)
|
||||
result = append(result, ma.Join(parts...))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveDnsaddr(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
|
||||
value, err := maddr.ValueForProtocol(DnsaddrProtocol.Code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s: %s", maddr.String(), err)
|
||||
}
|
||||
|
||||
trailer := ma.Split(maddr)[1:]
|
||||
|
||||
result := []ma.Multiaddr{}
|
||||
records, err := r.Backend.LookupTXT(ctx, "_dnsaddr."+value)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
rv := strings.Split(r, "dnsaddr=")
|
||||
if len(rv) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
rmaddr, err := ma.NewMultiaddr(rv[1])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if matchDnsaddr(rmaddr, trailer) {
|
||||
result = append(result, rmaddr)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XXX probably insecure
|
||||
func matchDnsaddr(maddr ma.Multiaddr, trailer []ma.Multiaddr) bool {
|
||||
parts := ma.Split(maddr)
|
||||
if ma.Join(parts[len(parts)-len(trailer):]...).Equal(ma.Join(trailer...)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
bin/gx*
|
||||
*.swp
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -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.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
export IPFS_API ?= v04x.ipfs.io
|
||||
|
||||
gx:
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# go-multiaddr-net
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/multiformats/go-multiaddr-net)
|
||||
[](https://travis-ci.org/multiformats/go-multiaddr-net)
|
||||
|
||||
<!---[](https://codecov.io/github/multiformats/go-multiaddr-net?branch=master)--->
|
||||
|
||||
> Multiaddress net tools
|
||||
|
||||
This package provides [Multiaddr](https://github.com/multiformats/go-multiaddr) specific versions of common functions in [stdlib](https://github.com/golang/go/tree/master/src)'s `net` package. This means wrappers of standard net symbols like `net.Dial` and `net.Listen`, as well
|
||||
as conversion to and from `net.Addr`.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
`go-multiaddr-net` is a standard Go module which can be installed with:
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multiaddr-net
|
||||
```
|
||||
|
||||
Note that `go-multiaddr-net` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project do:
|
||||
|
||||
```sh
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
cd <your-project-repository>
|
||||
gx init
|
||||
gx import github.com/multiformats/go-multiaddr-net
|
||||
gx install --global
|
||||
gx-go --rewrite
|
||||
```
|
||||
|
||||
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
|
||||
|
||||
For further usage, see the docs:
|
||||
|
||||
- `multiaddr/net`: https://godoc.org/github.com/multiformats/go-multiaddr-net
|
||||
- `multiaddr`: https://godoc.org/github.com/multiformats/go-multiaddr
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multiaddr-net/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2014 Juan Batiz-Benet
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
package manet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
madns "github.com/multiformats/go-multiaddr-dns"
|
||||
)
|
||||
|
||||
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")
|
||||
|
||||
// FromNetAddr converts a net.Addr type to a Multiaddr.
|
||||
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
return defaultCodecs.FromNetAddr(a)
|
||||
}
|
||||
|
||||
// FromNetAddr converts a net.Addr to Multiaddress.
|
||||
func (cm *CodecMap) FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
if a == nil {
|
||||
return nil, fmt.Errorf("nil multiaddr")
|
||||
}
|
||||
p, err := cm.getAddrParser(a.Network())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p(a)
|
||||
}
|
||||
|
||||
// ToNetAddr converts a Multiaddr to a net.Addr
|
||||
// Must be ThinWaist. acceptable protocol stacks are:
|
||||
// /ip{4,6}/{tcp, udp}
|
||||
func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
|
||||
return defaultCodecs.ToNetAddr(maddr)
|
||||
}
|
||||
|
||||
// ToNetAddr converts a Multiaddress to a standard net.Addr.
|
||||
func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
|
||||
protos := maddr.Protocols()
|
||||
final := protos[len(protos)-1]
|
||||
|
||||
p, err := cm.getMaddrParser(final.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p(maddr)
|
||||
}
|
||||
|
||||
func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
|
||||
network, host, err := DialArgs(maddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return net.ResolveTCPAddr(network, host)
|
||||
case "udp", "udp4", "udp6":
|
||||
return net.ResolveUDPAddr(network, host)
|
||||
case "ip", "ip4", "ip6":
|
||||
return net.ResolveIPAddr(network, host)
|
||||
case "unix":
|
||||
return net.ResolveUnixAddr(network, host)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("network not supported: %s", network)
|
||||
}
|
||||
|
||||
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
return ma.NewComponent("ip4", ip.String())
|
||||
case ip.To16() != nil:
|
||||
ip6, err := ma.NewComponent("ip6", ip.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if zone == "" {
|
||||
return ip6, nil
|
||||
} else {
|
||||
zone, err := ma.NewComponent("ip6zone", zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zone.Encapsulate(ip6), nil
|
||||
}
|
||||
default:
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
}
|
||||
|
||||
// FromIP converts a net.IP type to a Multiaddr.
|
||||
func FromIP(ip net.IP) (ma.Multiaddr, error) {
|
||||
return FromIPAndZone(ip, "")
|
||||
}
|
||||
|
||||
// DialArgs is a convenience function that returns network and address as
|
||||
// expected by net.Dial. See https://godoc.org/net#Dial for an overview of
|
||||
// possible return values (we do not support the unixpacket ones yet). Unix
|
||||
// addresses do not, at present, compose.
|
||||
func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||
var (
|
||||
zone, network, ip, port string
|
||||
err error
|
||||
hostname bool
|
||||
)
|
||||
|
||||
ma.ForEach(m, func(c ma.Component) bool {
|
||||
switch network {
|
||||
case "":
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP6ZONE:
|
||||
if zone != "" {
|
||||
err = fmt.Errorf("%s has multiple zones", m)
|
||||
return false
|
||||
}
|
||||
zone = c.Value()
|
||||
return true
|
||||
case ma.P_IP6:
|
||||
network = "ip6"
|
||||
ip = c.Value()
|
||||
return true
|
||||
case ma.P_IP4:
|
||||
if zone != "" {
|
||||
err = fmt.Errorf("%s has ip4 with zone", m)
|
||||
return false
|
||||
}
|
||||
network = "ip4"
|
||||
ip = c.Value()
|
||||
return true
|
||||
case madns.Dns4Protocol.Code:
|
||||
network = "ip4"
|
||||
hostname = true
|
||||
ip = c.Value()
|
||||
return true
|
||||
case madns.Dns6Protocol.Code:
|
||||
network = "ip6"
|
||||
hostname = true
|
||||
ip = c.Value()
|
||||
return true
|
||||
case ma.P_UNIX:
|
||||
network = "unix"
|
||||
ip = c.Value()
|
||||
return false
|
||||
}
|
||||
case "ip4":
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_UDP:
|
||||
network = "udp4"
|
||||
case ma.P_TCP:
|
||||
network = "tcp4"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
port = c.Value()
|
||||
case "ip6":
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_UDP:
|
||||
network = "udp6"
|
||||
case ma.P_TCP:
|
||||
network = "tcp6"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
port = c.Value()
|
||||
}
|
||||
// Done.
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "ip6":
|
||||
if zone != "" {
|
||||
ip += "%" + zone
|
||||
}
|
||||
fallthrough
|
||||
case "ip4":
|
||||
return network, ip, nil
|
||||
case "tcp4", "udp4":
|
||||
return network, ip + ":" + port, nil
|
||||
case "tcp6", "udp6":
|
||||
if zone != "" {
|
||||
ip += "%" + zone
|
||||
}
|
||||
if hostname {
|
||||
return network, ip + ":" + port, nil
|
||||
}
|
||||
return network, "[" + ip + "]" + ":" + port, nil
|
||||
case "unix":
|
||||
return network, ip, nil
|
||||
default:
|
||||
return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
|
||||
}
|
||||
}
|
||||
|
||||
func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
ac, ok := a.(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Get IP Addr
|
||||
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Get TCP Addr
|
||||
tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Encapsulate
|
||||
return ipm.Encapsulate(tcpm), nil
|
||||
}
|
||||
|
||||
func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
ac, ok := a.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Get IP Addr
|
||||
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Get UDP Addr
|
||||
udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
|
||||
// Encapsulate
|
||||
return ipm.Encapsulate(udpm), nil
|
||||
}
|
||||
|
||||
func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
ac, ok := a.(*net.IPAddr)
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
return FromIPAndZone(ac.IP, ac.Zone)
|
||||
}
|
||||
|
||||
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
ac, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
return FromIP(ac.IP)
|
||||
}
|
||||
|
||||
func parseUnixNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
ac, ok := a.(*net.UnixAddr)
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
cleaned := filepath.Clean(ac.Name)
|
||||
return ma.NewComponent("unix", cleaned)
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Package manet provides Multiaddr specific versions of common
|
||||
// functions in stdlib's net package. This means wrappers of
|
||||
// standard net symbols like net.Dial and net.Listen, as well
|
||||
// as conversion to/from net.Addr.
|
||||
package manet
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
module github.com/multiformats/go-multiaddr-net
|
||||
|
||||
require (
|
||||
github.com/multiformats/go-multiaddr v0.0.1
|
||||
github.com/multiformats/go-multiaddr-dns v0.0.1
|
||||
)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-multiaddr v0.0.1 h1:/QUV3VBMDI6pi6xfiw7lr6xhDWWvQKn9udPn68kLSdY=
|
||||
github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
|
||||
github.com/multiformats/go-multiaddr-dns v0.0.1 h1:jQt9c6tDSdQLIlBo4tXYx7QUHCPjxsB1zXcag/2S7zc=
|
||||
github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
|
||||
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
|
||||
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package manet
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// Loopback Addresses
|
||||
var (
|
||||
// IP4Loopback is the ip4 loopback multiaddr
|
||||
IP4Loopback = ma.StringCast("/ip4/127.0.0.1")
|
||||
|
||||
// IP6Loopback is the ip6 loopback multiaddr
|
||||
IP6Loopback = ma.StringCast("/ip6/::1")
|
||||
|
||||
// IP4MappedIP6Loopback is the IPv4 Mapped IPv6 loopback address.
|
||||
IP4MappedIP6Loopback = ma.StringCast("/ip6/::ffff:127.0.0.1")
|
||||
)
|
||||
|
||||
// Unspecified Addresses (used for )
|
||||
var (
|
||||
IP4Unspecified = ma.StringCast("/ip4/0.0.0.0")
|
||||
IP6Unspecified = ma.StringCast("/ip6/::")
|
||||
)
|
||||
|
||||
// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
|
||||
// This means: /{IP4, IP6}[/{TCP, UDP}]
|
||||
func IsThinWaist(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
p := m.Protocols()
|
||||
|
||||
// nothing? not even a waist.
|
||||
if len(p) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
|
||||
return false
|
||||
}
|
||||
|
||||
// only IP? still counts.
|
||||
if len(p) == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
switch p[1].Code {
|
||||
case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsIPLoopback returns whether a Multiaddr starts with a "Loopback" IP address
|
||||
// This means either /ip4/127.*.*.*/*, /ip6/::1/*, or /ip6/::ffff:127.*.*.*.*/*,
|
||||
// or /ip6zone/<any value>/ip6/<one of the preceding ip6 values>/*
|
||||
func IsIPLoopback(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
c, _ := ma.SplitFirst(m)
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP4, ma.P_IP6:
|
||||
return net.IP(c.RawValue()).IsLoopback()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIP6LinkLocal returns whether a Multiaddr starts with an IPv6 link-local
|
||||
// multiaddress (with zero or one leading zone). These addresses are non
|
||||
// routable.
|
||||
func IsIP6LinkLocal(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
c, _ := ma.SplitFirst(m)
|
||||
if c == nil || c.Protocol().Code != ma.P_IP6 {
|
||||
return false
|
||||
}
|
||||
ip := net.IP(c.RawValue())
|
||||
return ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
|
||||
}
|
||||
|
||||
// IsIPUnspecified returns whether a Multiaddr starts with an Unspecified IP address
|
||||
// This means either /ip4/0.0.0.0/* or /ip6/::/*
|
||||
func IsIPUnspecified(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
c, _ := ma.SplitFirst(m)
|
||||
return net.IP(c.RawValue()).IsUnspecified()
|
||||
}
|
||||
|
||||
// If m matches [zone,ip6,...], return [ip6,...]
|
||||
// else if m matches [], [zone], or [zone,...], return nil
|
||||
// else return m
|
||||
func zoneless(m ma.Multiaddr) ma.Multiaddr {
|
||||
head, tail := ma.SplitFirst(m)
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
if head.Protocol().Code == ma.P_IP6ZONE {
|
||||
if tail == nil {
|
||||
return nil
|
||||
}
|
||||
tailhead, _ := ma.SplitFirst(tail)
|
||||
if tailhead.Protocol().Code != ma.P_IP6 {
|
||||
return nil
|
||||
}
|
||||
return tail
|
||||
} else {
|
||||
return m
|
||||
}
|
||||
}
|
||||
+427
@@ -0,0 +1,427 @@
|
||||
// Package manet provides Multiaddr
|
||||
// (https://github.com/multiformats/go-multiaddr) specific versions of common
|
||||
// functions in Go's standard `net` package. This means wrappers of standard
|
||||
// net symbols like `net.Dial` and `net.Listen`, as well as conversion to
|
||||
// and from `net.Addr`.
|
||||
package manet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// Conn is the equivalent of a net.Conn object. It is the
|
||||
// result of calling the Dial or Listen functions in this
|
||||
// package, with associated local and remote Multiaddrs.
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
|
||||
// LocalMultiaddr returns the local Multiaddr associated
|
||||
// with this connection
|
||||
LocalMultiaddr() ma.Multiaddr
|
||||
|
||||
// RemoteMultiaddr returns the remote Multiaddr associated
|
||||
// with this connection
|
||||
RemoteMultiaddr() ma.Multiaddr
|
||||
}
|
||||
|
||||
type halfOpen interface {
|
||||
net.Conn
|
||||
CloseRead() error
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func wrap(nconn net.Conn, laddr, raddr ma.Multiaddr) Conn {
|
||||
endpts := maEndpoints{
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
}
|
||||
// This sucks. However, it's the only way to reliably expose the
|
||||
// underlying methods. This way, users that need access to, e.g.,
|
||||
// CloseRead and CloseWrite, can do so via type assertions.
|
||||
switch nconn := nconn.(type) {
|
||||
case *net.TCPConn:
|
||||
return &struct {
|
||||
*net.TCPConn
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
case *net.UDPConn:
|
||||
return &struct {
|
||||
*net.UDPConn
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
case *net.IPConn:
|
||||
return &struct {
|
||||
*net.IPConn
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
case *net.UnixConn:
|
||||
return &struct {
|
||||
*net.UnixConn
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
case halfOpen:
|
||||
return &struct {
|
||||
halfOpen
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
default:
|
||||
return &struct {
|
||||
net.Conn
|
||||
maEndpoints
|
||||
}{nconn, endpts}
|
||||
}
|
||||
}
|
||||
|
||||
// WrapNetConn wraps a net.Conn object with a Multiaddr friendly Conn.
|
||||
//
|
||||
// This function does it's best to avoid "hiding" methods exposed by the wrapped
|
||||
// type. Guarantees:
|
||||
//
|
||||
// * If the wrapped connection exposes the "half-open" closer methods
|
||||
// (CloseWrite, CloseRead), these will be available on the wrapped connection
|
||||
// via type assertions.
|
||||
// * If the wrapped connection is a UnixConn, IPConn, TCPConn, or UDPConn, all
|
||||
// methods on these wrapped connections will be available via type assertions.
|
||||
func WrapNetConn(nconn net.Conn) (Conn, error) {
|
||||
if nconn == nil {
|
||||
return nil, fmt.Errorf("failed to convert nconn.LocalAddr: nil")
|
||||
}
|
||||
|
||||
laddr, err := FromNetAddr(nconn.LocalAddr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert nconn.LocalAddr: %s", err)
|
||||
}
|
||||
|
||||
raddr, err := FromNetAddr(nconn.RemoteAddr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert nconn.RemoteAddr: %s", err)
|
||||
}
|
||||
|
||||
return wrap(nconn, laddr, raddr), nil
|
||||
}
|
||||
|
||||
type maEndpoints struct {
|
||||
laddr ma.Multiaddr
|
||||
raddr ma.Multiaddr
|
||||
}
|
||||
|
||||
// LocalMultiaddr returns the local address associated with
|
||||
// this connection
|
||||
func (c *maEndpoints) LocalMultiaddr() ma.Multiaddr {
|
||||
return c.laddr
|
||||
}
|
||||
|
||||
// RemoteMultiaddr returns the remote address associated with
|
||||
// this connection
|
||||
func (c *maEndpoints) RemoteMultiaddr() ma.Multiaddr {
|
||||
return c.raddr
|
||||
}
|
||||
|
||||
// Dialer contains options for connecting to an address. It
|
||||
// is effectively the same as net.Dialer, but its LocalAddr
|
||||
// and RemoteAddr options are Multiaddrs, instead of net.Addrs.
|
||||
type Dialer struct {
|
||||
|
||||
// Dialer is just an embedded net.Dialer, with all its options.
|
||||
net.Dialer
|
||||
|
||||
// LocalAddr is the local address to use when dialing an
|
||||
// address. The address must be of a compatible type for the
|
||||
// network being dialed.
|
||||
// If nil, a local address is automatically chosen.
|
||||
LocalAddr ma.Multiaddr
|
||||
}
|
||||
|
||||
// Dial connects to a remote address, using the options of the
|
||||
// Dialer. Dialer uses an underlying net.Dialer to Dial a
|
||||
// net.Conn, then wraps that in a Conn object (with local and
|
||||
// remote Multiaddrs).
|
||||
func (d *Dialer) Dial(remote ma.Multiaddr) (Conn, error) {
|
||||
return d.DialContext(context.Background(), remote)
|
||||
}
|
||||
|
||||
// DialContext allows to provide a custom context to Dial().
|
||||
func (d *Dialer) DialContext(ctx context.Context, remote ma.Multiaddr) (Conn, error) {
|
||||
// if a LocalAddr is specified, use it on the embedded dialer.
|
||||
if d.LocalAddr != nil {
|
||||
// convert our multiaddr to net.Addr friendly
|
||||
naddr, err := ToNetAddr(d.LocalAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set the dialer's LocalAddr as naddr
|
||||
d.Dialer.LocalAddr = naddr
|
||||
}
|
||||
|
||||
// get the net.Dial friendly arguments from the remote addr
|
||||
rnet, rnaddr, err := DialArgs(remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ok, Dial!
|
||||
var nconn net.Conn
|
||||
switch rnet {
|
||||
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix":
|
||||
nconn, err = d.Dialer.DialContext(ctx, rnet, rnaddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized network: %s", rnet)
|
||||
}
|
||||
|
||||
// get local address (pre-specified or assigned within net.Conn)
|
||||
local := d.LocalAddr
|
||||
// This block helps us avoid parsing addresses in transports (such as unix
|
||||
// sockets) that don't have local addresses when dialing out.
|
||||
if local == nil && nconn.LocalAddr().String() != "" {
|
||||
local, err = FromNetAddr(nconn.LocalAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return wrap(nconn, local, remote), nil
|
||||
}
|
||||
|
||||
// Dial connects to a remote address. It uses an underlying net.Conn,
|
||||
// then wraps it in a Conn object (with local and remote Multiaddrs).
|
||||
func Dial(remote ma.Multiaddr) (Conn, error) {
|
||||
return (&Dialer{}).Dial(remote)
|
||||
}
|
||||
|
||||
// A Listener is a generic network listener for stream-oriented protocols.
|
||||
// it uses an embedded net.Listener, overriding net.Listener.Accept to
|
||||
// return a Conn and providing Multiaddr.
|
||||
type Listener interface {
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
// Returns a Multiaddr friendly Conn
|
||||
Accept() (Conn, error)
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
Close() error
|
||||
|
||||
// Multiaddr returns the listener's (local) Multiaddr.
|
||||
Multiaddr() ma.Multiaddr
|
||||
|
||||
// Addr returns the net.Listener's network address.
|
||||
Addr() net.Addr
|
||||
}
|
||||
|
||||
type netListenerAdapter struct {
|
||||
Listener
|
||||
}
|
||||
|
||||
func (nla *netListenerAdapter) Accept() (net.Conn, error) {
|
||||
return nla.Listener.Accept()
|
||||
}
|
||||
|
||||
// NetListener turns this Listener into a net.Listener.
|
||||
//
|
||||
// * Connections returned from Accept implement multiaddr-net Conn.
|
||||
// * Calling WrapNetListener on the net.Listener returned by this function will
|
||||
// return the original (underlying) multiaddr-net Listener.
|
||||
func NetListener(l Listener) net.Listener {
|
||||
return &netListenerAdapter{l}
|
||||
}
|
||||
|
||||
// maListener implements Listener
|
||||
type maListener struct {
|
||||
net.Listener
|
||||
laddr ma.Multiaddr
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
// Returns a Multiaddr friendly Conn
|
||||
func (l *maListener) Accept() (Conn, error) {
|
||||
nconn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raddr ma.Multiaddr
|
||||
// This block protects us in transports (i.e. unix sockets) that don't have
|
||||
// remote addresses for inbound connections.
|
||||
if nconn.RemoteAddr().String() != "" {
|
||||
raddr, err = FromNetAddr(nconn.RemoteAddr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert conn.RemoteAddr: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return wrap(nconn, l.laddr, raddr), nil
|
||||
}
|
||||
|
||||
// Multiaddr returns the listener's (local) Multiaddr.
|
||||
func (l *maListener) Multiaddr() ma.Multiaddr {
|
||||
return l.laddr
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *maListener) Addr() net.Addr {
|
||||
return l.Listener.Addr()
|
||||
}
|
||||
|
||||
// Listen announces on the local network address laddr.
|
||||
// The Multiaddr must be a "ThinWaist" stream-oriented network:
|
||||
// ip4/tcp, ip6/tcp, (TODO: unix, unixpacket)
|
||||
// See Dial for the syntax of laddr.
|
||||
func Listen(laddr ma.Multiaddr) (Listener, error) {
|
||||
|
||||
// get the net.Listen friendly arguments from the remote addr
|
||||
lnet, lnaddr, err := DialArgs(laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nl, err := net.Listen(lnet, lnaddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we want to fetch the new multiaddr from the listener, as it may
|
||||
// have resolved to some other value. WrapNetListener does it for us.
|
||||
return WrapNetListener(nl)
|
||||
}
|
||||
|
||||
// WrapNetListener wraps a net.Listener with a manet.Listener.
|
||||
func WrapNetListener(nl net.Listener) (Listener, error) {
|
||||
if nla, ok := nl.(*netListenerAdapter); ok {
|
||||
return nla.Listener, nil
|
||||
}
|
||||
|
||||
laddr, err := FromNetAddr(nl.Addr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &maListener{
|
||||
Listener: nl,
|
||||
laddr: laddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// A PacketConn is a generic packet oriented network connection which uses an
|
||||
// underlying net.PacketConn, wrapped with the locally bound Multiaddr.
|
||||
type PacketConn interface {
|
||||
Connection() net.PacketConn
|
||||
|
||||
Multiaddr() ma.Multiaddr
|
||||
|
||||
ReadFrom(b []byte) (int, ma.Multiaddr, error)
|
||||
WriteTo(b []byte, maddr ma.Multiaddr) (int, error)
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
// maPacketConn implements PacketConn
|
||||
type maPacketConn struct {
|
||||
net.PacketConn
|
||||
laddr ma.Multiaddr
|
||||
}
|
||||
|
||||
// Connection returns the embedded net.PacketConn.
|
||||
func (l *maPacketConn) Connection() net.PacketConn {
|
||||
return l.PacketConn
|
||||
}
|
||||
|
||||
// Multiaddr returns the bound local Multiaddr.
|
||||
func (l *maPacketConn) Multiaddr() ma.Multiaddr {
|
||||
return l.laddr
|
||||
}
|
||||
|
||||
func (l *maPacketConn) ReadFrom(b []byte) (int, ma.Multiaddr, error) {
|
||||
n, addr, err := l.PacketConn.ReadFrom(b)
|
||||
maddr, _ := FromNetAddr(addr)
|
||||
return n, maddr, err
|
||||
}
|
||||
|
||||
func (l *maPacketConn) WriteTo(b []byte, maddr ma.Multiaddr) (int, error) {
|
||||
addr, err := ToNetAddr(maddr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return l.PacketConn.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
// ListenPacket announces on the local network address laddr.
|
||||
// The Multiaddr must be a packet driven network, like udp4 or udp6.
|
||||
// See Dial for the syntax of laddr.
|
||||
func ListenPacket(laddr ma.Multiaddr) (PacketConn, error) {
|
||||
lnet, lnaddr, err := DialArgs(laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pc, err := net.ListenPacket(lnet, lnaddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We want to fetch the new multiaddr from the listener, as it may
|
||||
// have resolved to some other value. WrapPacketConn does this.
|
||||
return WrapPacketConn(pc)
|
||||
}
|
||||
|
||||
// WrapPacketConn wraps a net.PacketConn with a manet.PacketConn.
|
||||
func WrapPacketConn(pc net.PacketConn) (PacketConn, error) {
|
||||
laddr, err := FromNetAddr(pc.LocalAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &maPacketConn{
|
||||
PacketConn: pc,
|
||||
laddr: laddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InterfaceMultiaddrs will return the addresses matching net.InterfaceAddrs
|
||||
func InterfaceMultiaddrs() ([]ma.Multiaddr, error) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maddrs := make([]ma.Multiaddr, len(addrs))
|
||||
for i, a := range addrs {
|
||||
maddrs[i], err = FromNetAddr(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return maddrs, nil
|
||||
}
|
||||
|
||||
// AddrMatch returns the Multiaddrs that match the protocol stack on addr
|
||||
func AddrMatch(match ma.Multiaddr, addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
|
||||
// we should match transports entirely.
|
||||
p1s := match.Protocols()
|
||||
|
||||
out := make([]ma.Multiaddr, 0, len(addrs))
|
||||
for _, a := range addrs {
|
||||
p2s := a.Protocols()
|
||||
if len(p1s) != len(p2s) {
|
||||
continue
|
||||
}
|
||||
|
||||
match := true
|
||||
for i, p2 := range p2s {
|
||||
if p1s[i].Code != p2.Code {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
out = append(out, a)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"author": "multiformats",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multiaddr-net"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multiaddr-net"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "multiformats",
|
||||
"hash": "QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL",
|
||||
"name": "go-multiaddr",
|
||||
"version": "1.4.1"
|
||||
},
|
||||
{
|
||||
"author": "lgierth",
|
||||
"hash": "QmU98UaAEh4WJAcir2qjfztU77JQ14kAwHNFkjUXHZA3Vy",
|
||||
"name": "go-multiaddr-dns",
|
||||
"version": "0.3.1"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.6.0",
|
||||
"language": "go",
|
||||
"license": "",
|
||||
"name": "go-multiaddr-net",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "1.7.2"
|
||||
}
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package manet
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// Private4 and Private6 are well-known private networks
|
||||
var Private4, Private6 []*net.IPNet
|
||||
var privateCIDR4 = []string{
|
||||
// localhost
|
||||
"127.0.0.0/8",
|
||||
// private networks
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
// link local
|
||||
"169.254.0.0/16",
|
||||
}
|
||||
var privateCIDR6 = []string{
|
||||
// localhost
|
||||
"::1/128",
|
||||
// ULA reserved
|
||||
"fc00::/7",
|
||||
// link local
|
||||
"fe80::/10",
|
||||
}
|
||||
|
||||
// Unroutable4 and Unroutable6 are well known unroutable address ranges
|
||||
var Unroutable4, Unroutable6 []*net.IPNet
|
||||
var unroutableCIDR4 = []string{
|
||||
"0.0.0.0/8",
|
||||
"192.0.0.0/26",
|
||||
"192.0.2.0/24",
|
||||
"192.88.99.0/24",
|
||||
"198.18.0.0/15",
|
||||
"198.51.100.0/24",
|
||||
"203.0.113.0/24",
|
||||
"224.0.0.0/4",
|
||||
"240.0.0.0/4",
|
||||
"255.255.255.255/32",
|
||||
}
|
||||
var unroutableCIDR6 = []string{
|
||||
"ff00::/8",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Private4 = parseCIDR(privateCIDR4)
|
||||
Private6 = parseCIDR(privateCIDR6)
|
||||
Unroutable4 = parseCIDR(unroutableCIDR4)
|
||||
Unroutable6 = parseCIDR(unroutableCIDR6)
|
||||
}
|
||||
|
||||
func parseCIDR(cidrs []string) []*net.IPNet {
|
||||
ipnets := make([]*net.IPNet, len(cidrs))
|
||||
for i, cidr := range cidrs {
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ipnets[i] = ipnet
|
||||
}
|
||||
return ipnets
|
||||
}
|
||||
|
||||
// IsPublicAddr retruns true if the IP part of the multiaddr is a publicly routable address
|
||||
func IsPublicAddr(a ma.Multiaddr) bool {
|
||||
isPublic := false
|
||||
ma.ForEach(a, func(c ma.Component) bool {
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP6ZONE:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
case ma.P_IP4:
|
||||
ip := net.IP(c.RawValue())
|
||||
isPublic = !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4)
|
||||
case ma.P_IP6:
|
||||
ip := net.IP(c.RawValue())
|
||||
isPublic = !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6)
|
||||
}
|
||||
return false
|
||||
})
|
||||
return isPublic
|
||||
}
|
||||
|
||||
// IsPrivateAddr returns true if the IP part of the mutiaddr is in a private network
|
||||
func IsPrivateAddr(a ma.Multiaddr) bool {
|
||||
isPrivate := false
|
||||
ma.ForEach(a, func(c ma.Component) bool {
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP6ZONE:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
case ma.P_IP4:
|
||||
isPrivate = inAddrRange(net.IP(c.RawValue()), Private4)
|
||||
case ma.P_IP6:
|
||||
isPrivate = inAddrRange(net.IP(c.RawValue()), Private6)
|
||||
}
|
||||
return false
|
||||
})
|
||||
return isPrivate
|
||||
}
|
||||
|
||||
func inAddrRange(ip net.IP, ipnets []*net.IPNet) bool {
|
||||
for _, ipnet := range ipnets {
|
||||
if ipnet.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package manet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// FromNetAddrFunc is a generic function which converts a net.Addr to Multiaddress
|
||||
type FromNetAddrFunc func(a net.Addr) (ma.Multiaddr, error)
|
||||
|
||||
// ToNetAddrFunc is a generic function which converts a Multiaddress to net.Addr
|
||||
type ToNetAddrFunc func(ma ma.Multiaddr) (net.Addr, error)
|
||||
|
||||
var defaultCodecs = NewCodecMap()
|
||||
|
||||
func init() {
|
||||
defaultCodecs.RegisterFromNetAddr(parseTCPNetAddr, "tcp", "tcp4", "tcp6")
|
||||
defaultCodecs.RegisterFromNetAddr(parseUDPNetAddr, "udp", "udp4", "udp6")
|
||||
defaultCodecs.RegisterFromNetAddr(parseIPNetAddr, "ip", "ip4", "ip6")
|
||||
defaultCodecs.RegisterFromNetAddr(parseIPPlusNetAddr, "ip+net")
|
||||
defaultCodecs.RegisterFromNetAddr(parseUnixNetAddr, "unix")
|
||||
|
||||
defaultCodecs.RegisterToNetAddr(parseBasicNetMaddr, "tcp", "udp", "ip6", "ip4", "unix")
|
||||
}
|
||||
|
||||
// CodecMap holds a map of NetCodecs indexed by their Protocol ID
|
||||
// along with parsers for the addresses they use.
|
||||
// It is used to keep a list of supported network address codecs (protocols
|
||||
// which addresses can be converted to and from multiaddresses).
|
||||
type CodecMap struct {
|
||||
codecs map[string]*NetCodec
|
||||
addrParsers map[string]FromNetAddrFunc
|
||||
maddrParsers map[string]ToNetAddrFunc
|
||||
lk sync.Mutex
|
||||
}
|
||||
|
||||
// NewCodecMap initializes and returns a CodecMap object.
|
||||
func NewCodecMap() *CodecMap {
|
||||
return &CodecMap{
|
||||
addrParsers: make(map[string]FromNetAddrFunc),
|
||||
maddrParsers: make(map[string]ToNetAddrFunc),
|
||||
}
|
||||
}
|
||||
|
||||
// NetCodec is used to identify a network codec, that is, a network type for
|
||||
// which we are able to translate multiaddresses into standard Go net.Addr
|
||||
// and back.
|
||||
//
|
||||
// Deprecated: Unfortunately, these mappings aren't one to one. This abstraction
|
||||
// assumes that multiple "networks" can map to a single multiaddr protocol but
|
||||
// not the reverse. For example, this abstraction supports `tcp6, tcp4, tcp ->
|
||||
// /tcp/` really well but doesn't support `ip -> {/ip4/, /ip6/}`.
|
||||
//
|
||||
// Please use `RegisterFromNetAddr` and `RegisterToNetAddr` directly.
|
||||
type NetCodec struct {
|
||||
// NetAddrNetworks is an array of strings that may be returned
|
||||
// by net.Addr.Network() calls on addresses belonging to this type
|
||||
NetAddrNetworks []string
|
||||
|
||||
// ProtocolName is the string value for Multiaddr address keys
|
||||
ProtocolName string
|
||||
|
||||
// ParseNetAddr parses a net.Addr belonging to this type into a multiaddr
|
||||
ParseNetAddr FromNetAddrFunc
|
||||
|
||||
// ConvertMultiaddr converts a multiaddr of this type back into a net.Addr
|
||||
ConvertMultiaddr ToNetAddrFunc
|
||||
|
||||
// Protocol returns the multiaddr protocol struct for this type
|
||||
Protocol ma.Protocol
|
||||
}
|
||||
|
||||
// RegisterNetCodec adds a new NetCodec to the default codecs.
|
||||
func RegisterNetCodec(a *NetCodec) {
|
||||
defaultCodecs.RegisterNetCodec(a)
|
||||
}
|
||||
|
||||
// RegisterNetCodec adds a new NetCodec to the CodecMap. This function is
|
||||
// thread safe.
|
||||
func (cm *CodecMap) RegisterNetCodec(a *NetCodec) {
|
||||
cm.lk.Lock()
|
||||
defer cm.lk.Unlock()
|
||||
for _, n := range a.NetAddrNetworks {
|
||||
cm.addrParsers[n] = a.ParseNetAddr
|
||||
}
|
||||
|
||||
cm.maddrParsers[a.ProtocolName] = a.ConvertMultiaddr
|
||||
}
|
||||
|
||||
// RegisterFromNetAddr registers a conversion from net.Addr instances to multiaddrs
|
||||
func (cm *CodecMap) RegisterFromNetAddr(from FromNetAddrFunc, networks ...string) {
|
||||
cm.lk.Lock()
|
||||
defer cm.lk.Unlock()
|
||||
|
||||
for _, n := range networks {
|
||||
cm.addrParsers[n] = from
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterToNetAddr registers a conversion from multiaddrs to net.Addr instances
|
||||
func (cm *CodecMap) RegisterToNetAddr(to ToNetAddrFunc, protocols ...string) {
|
||||
cm.lk.Lock()
|
||||
defer cm.lk.Unlock()
|
||||
|
||||
for _, p := range protocols {
|
||||
cm.maddrParsers[p] = to
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *CodecMap) getAddrParser(net string) (FromNetAddrFunc, error) {
|
||||
cm.lk.Lock()
|
||||
defer cm.lk.Unlock()
|
||||
|
||||
parser, ok := cm.addrParsers[net]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown network %v", net)
|
||||
}
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func (cm *CodecMap) getMaddrParser(name string) (ToNetAddrFunc, error) {
|
||||
cm.lk.Lock()
|
||||
defer cm.lk.Unlock()
|
||||
p, ok := cm.maddrParsers[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("network not supported: %s", name)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.vscode/
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -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.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
gx:
|
||||
go get github.com/whyrusleeping/gx
|
||||
go get github.com/whyrusleeping/gx-go
|
||||
|
||||
covertools:
|
||||
go get golang.org/x/tools/cmd/cover
|
||||
|
||||
deps: gx covertools
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
|
||||
publish:
|
||||
gx-go rewrite --undo
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
# go-multiaddr
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/multiformats/go-multiaddr)
|
||||
[](https://travis-ci.org/multiformats/go-multiaddr)
|
||||
[](https://codecov.io/github/multiformats/go-multiaddr?branch=master)
|
||||
|
||||
> [multiaddr](https://github.com/multiformats/multiaddr) implementation in go
|
||||
|
||||
Multiaddr is a standard way to represent addresses that:
|
||||
|
||||
- Support any standard network protocols.
|
||||
- Self-describe (include protocols).
|
||||
- Have a binary packed format.
|
||||
- Have a nice string representation.
|
||||
- Encapsulate well.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Example](#example)
|
||||
- [Simple](#simple)
|
||||
- [Protocols](#protocols)
|
||||
- [En/decapsulate](#endecapsulate)
|
||||
- [Tunneling](#tunneling)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multiaddr
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Example
|
||||
|
||||
#### Simple
|
||||
|
||||
```go
|
||||
import ma "github.com/multiformats/go-multiaddr"
|
||||
|
||||
// construct from a string (err signals parse failure)
|
||||
m1, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
|
||||
// construct from bytes (err signals parse failure)
|
||||
m2, err := ma.NewMultiaddrBytes(m1.Bytes())
|
||||
|
||||
// true
|
||||
strings.Equal(m1.String(), "/ip4/127.0.0.1/udp/1234")
|
||||
strings.Equal(m1.String(), m2.String())
|
||||
bytes.Equal(m1.Bytes(), m2.Bytes())
|
||||
m1.Equal(m2)
|
||||
m2.Equal(m1)
|
||||
```
|
||||
|
||||
#### Protocols
|
||||
|
||||
```go
|
||||
// get the multiaddr protocol description objects
|
||||
m1.Protocols()
|
||||
// []Protocol{
|
||||
// Protocol{ Code: 4, Name: 'ip4', Size: 32},
|
||||
// Protocol{ Code: 17, Name: 'udp', Size: 16},
|
||||
// }
|
||||
```
|
||||
|
||||
#### En/decapsulate
|
||||
|
||||
```go
|
||||
import ma "github.com/multiformats/go-multiaddr"
|
||||
|
||||
m, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
// <Multiaddr /ip4/127.0.0.1/udp/1234>
|
||||
|
||||
sctpMA, err := ma.NewMultiaddr("/sctp/5678")
|
||||
|
||||
m.Encapsulate(sctpMA)
|
||||
// <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
|
||||
|
||||
udpMA, err := ma.NewMultiaddr("/udp/1234")
|
||||
|
||||
m.Decapsulate(udpMA) // up to + inc last occurrence of subaddr
|
||||
// <Multiaddr /ip4/127.0.0.1>
|
||||
```
|
||||
|
||||
#### Tunneling
|
||||
|
||||
Multiaddr allows expressing tunnels very nicely.
|
||||
|
||||
```js
|
||||
printer, _ := ma.NewMultiaddr("/ip4/192.168.0.13/tcp/80")
|
||||
proxy, _ := ma.NewMultiaddr("/ip4/10.20.30.40/tcp/443")
|
||||
printerOverProxy := proxy.Encapsulate(printer)
|
||||
// /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80
|
||||
|
||||
proxyAgain := printerOverProxy.Decapsulate(printer)
|
||||
// /ip4/10.20.30.40/tcp/443
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multiaddr/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2014 Juan Batiz-Benet
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stringToBytes(s string) ([]byte, error) {
|
||||
|
||||
// consume trailing slashes
|
||||
s = strings.TrimRight(s, "/")
|
||||
|
||||
var b bytes.Buffer
|
||||
sp := strings.Split(s, "/")
|
||||
|
||||
if sp[0] != "" {
|
||||
return nil, fmt.Errorf("invalid multiaddr, must begin with /")
|
||||
}
|
||||
|
||||
// consume first empty elem
|
||||
sp = sp[1:]
|
||||
|
||||
for len(sp) > 0 {
|
||||
name := sp[0]
|
||||
p := ProtocolWithName(name)
|
||||
if p.Code == 0 {
|
||||
return nil, fmt.Errorf("no protocol with name %s", sp[0])
|
||||
}
|
||||
_, _ = b.Write(CodeToVarint(p.Code))
|
||||
sp = sp[1:]
|
||||
|
||||
if p.Size == 0 { // no length.
|
||||
continue
|
||||
}
|
||||
|
||||
if len(sp) < 1 {
|
||||
return nil, fmt.Errorf("protocol requires address, none given: %s", name)
|
||||
}
|
||||
|
||||
if p.Path {
|
||||
// it's a path protocol (terminal).
|
||||
// consume the rest of the address as the next component.
|
||||
sp = []string{"/" + strings.Join(sp, "/")}
|
||||
}
|
||||
|
||||
a, err := p.Transcoder.StringToBytes(sp[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err)
|
||||
}
|
||||
if p.Size < 0 { // varint size.
|
||||
_, _ = b.Write(CodeToVarint(len(a)))
|
||||
}
|
||||
b.Write(a)
|
||||
sp = sp[1:]
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func validateBytes(b []byte) (err error) {
|
||||
for len(b) > 0 {
|
||||
code, n, err := ReadVarintCode(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = b[n:]
|
||||
p := ProtocolWithCode(code)
|
||||
if p.Code == 0 {
|
||||
return fmt.Errorf("no protocol with code %d", code)
|
||||
}
|
||||
|
||||
if p.Size == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
n, size, err := sizeForAddr(p, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = b[n:]
|
||||
|
||||
if len(b) < size || size < 0 {
|
||||
return fmt.Errorf("invalid value for size %d", len(b))
|
||||
}
|
||||
|
||||
err = p.Transcoder.ValidateBytes(b[:size])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = b[size:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readComponent(b []byte) (int, Component, error) {
|
||||
var offset int
|
||||
code, n, err := ReadVarintCode(b)
|
||||
if err != nil {
|
||||
return 0, Component{}, err
|
||||
}
|
||||
offset += n
|
||||
|
||||
p := ProtocolWithCode(code)
|
||||
if p.Code == 0 {
|
||||
return 0, Component{}, fmt.Errorf("no protocol with code %d", code)
|
||||
}
|
||||
|
||||
if p.Size == 0 {
|
||||
return offset, Component{
|
||||
bytes: b[:offset],
|
||||
offset: offset,
|
||||
protocol: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
n, size, err := sizeForAddr(p, b[offset:])
|
||||
if err != nil {
|
||||
return 0, Component{}, err
|
||||
}
|
||||
|
||||
offset += n
|
||||
|
||||
if len(b[offset:]) < size || size < 0 {
|
||||
return 0, Component{}, fmt.Errorf("invalid value for size %d", len(b[offset:]))
|
||||
}
|
||||
|
||||
return offset + size, Component{
|
||||
bytes: b[:offset+size],
|
||||
protocol: p,
|
||||
offset: offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func bytesToString(b []byte) (ret string, err error) {
|
||||
var buf strings.Builder
|
||||
|
||||
for len(b) > 0 {
|
||||
n, c, err := readComponent(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b = b[n:]
|
||||
c.writeTo(&buf)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func sizeForAddr(p Protocol, b []byte) (skip, size int, err error) {
|
||||
switch {
|
||||
case p.Size > 0:
|
||||
return 0, (p.Size / 8), nil
|
||||
case p.Size == 0:
|
||||
return 0, 0, nil
|
||||
default:
|
||||
size, n, err := ReadVarintCode(b)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return n, size, nil
|
||||
}
|
||||
}
|
||||
|
||||
func bytesSplit(b []byte) ([][]byte, error) {
|
||||
var ret [][]byte
|
||||
for len(b) > 0 {
|
||||
code, n, err := ReadVarintCode(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := ProtocolWithCode(code)
|
||||
if p.Code == 0 {
|
||||
return nil, fmt.Errorf("no protocol with code %d", b[0])
|
||||
}
|
||||
|
||||
n2, size, err := sizeForAddr(p, b[n:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := n + n2 + size
|
||||
ret = append(ret, b[:length])
|
||||
b = b[length:]
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Component is a single multiaddr Component.
|
||||
type Component struct {
|
||||
bytes []byte
|
||||
protocol Protocol
|
||||
offset int
|
||||
}
|
||||
|
||||
func (c *Component) Bytes() []byte {
|
||||
return c.bytes
|
||||
}
|
||||
|
||||
func (c *Component) MarshalBinary() ([]byte, error) {
|
||||
return c.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *Component) UnmarshalBinary(data []byte) error {
|
||||
_, comp, err := readComponent(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*c = comp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Component) MarshalText() ([]byte, error) {
|
||||
return []byte(c.String()), nil
|
||||
}
|
||||
|
||||
func (c *Component) UnmarshalText(data []byte) error {
|
||||
bytes, err := stringToBytes(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, comp, err := readComponent(bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*c = comp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Component) MarshalJSON() ([]byte, error) {
|
||||
txt, err := c.MarshalText()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(string(txt))
|
||||
}
|
||||
|
||||
func (m *Component) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.UnmarshalText([]byte(v))
|
||||
}
|
||||
|
||||
func (c *Component) Equal(o Multiaddr) bool {
|
||||
return bytes.Equal(c.bytes, o.Bytes())
|
||||
}
|
||||
|
||||
func (c *Component) Protocols() []Protocol {
|
||||
return []Protocol{c.protocol}
|
||||
}
|
||||
|
||||
func (c *Component) Decapsulate(o Multiaddr) Multiaddr {
|
||||
if c.Equal(o) {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Component) Encapsulate(o Multiaddr) Multiaddr {
|
||||
m := &multiaddr{bytes: c.bytes}
|
||||
return m.Encapsulate(o)
|
||||
}
|
||||
|
||||
func (c *Component) ValueForProtocol(code int) (string, error) {
|
||||
if c.protocol.Code != code {
|
||||
return "", ErrProtocolNotFound
|
||||
}
|
||||
return c.Value(), nil
|
||||
}
|
||||
|
||||
func (c *Component) Protocol() Protocol {
|
||||
return c.protocol
|
||||
}
|
||||
|
||||
func (c *Component) RawValue() []byte {
|
||||
return c.bytes[c.offset:]
|
||||
}
|
||||
|
||||
func (c *Component) Value() string {
|
||||
if c.protocol.Transcoder == nil {
|
||||
return ""
|
||||
}
|
||||
value, err := c.protocol.Transcoder.BytesToString(c.bytes[c.offset:])
|
||||
if err != nil {
|
||||
// This Component must have been checked.
|
||||
panic(err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *Component) String() string {
|
||||
var b strings.Builder
|
||||
c.writeTo(&b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// writeTo is an efficient, private function for string-formatting a multiaddr.
|
||||
// Trust me, we tend to allocate a lot when doing this.
|
||||
func (c *Component) writeTo(b *strings.Builder) {
|
||||
b.WriteByte('/')
|
||||
b.WriteString(c.protocol.Name)
|
||||
value := c.Value()
|
||||
if len(value) == 0 {
|
||||
return
|
||||
}
|
||||
if !(c.protocol.Path && value[0] == '/') {
|
||||
b.WriteByte('/')
|
||||
}
|
||||
b.WriteString(value)
|
||||
}
|
||||
|
||||
// NewComponent constructs a new multiaddr component
|
||||
func NewComponent(protocol, value string) (*Component, error) {
|
||||
p := ProtocolWithName(protocol)
|
||||
if p.Code == 0 {
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
|
||||
}
|
||||
if p.Transcoder != nil {
|
||||
bts, err := p.Transcoder.StringToBytes(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newComponent(p, bts), nil
|
||||
} else if value != "" {
|
||||
return nil, fmt.Errorf("protocol %s doesn't take a value", p.Name)
|
||||
}
|
||||
return newComponent(p, nil), nil
|
||||
// TODO: handle path /?
|
||||
}
|
||||
|
||||
func newComponent(protocol Protocol, bvalue []byte) *Component {
|
||||
size := len(bvalue)
|
||||
size += len(protocol.VCode)
|
||||
if protocol.Size < 0 {
|
||||
size += VarintSize(len(bvalue))
|
||||
}
|
||||
maddr := make([]byte, size)
|
||||
var offset int
|
||||
offset += copy(maddr[offset:], protocol.VCode)
|
||||
if protocol.Size < 0 {
|
||||
offset += binary.PutUvarint(maddr[offset:], uint64(len(bvalue)))
|
||||
}
|
||||
copy(maddr[offset:], bvalue)
|
||||
|
||||
// For debugging
|
||||
if len(maddr) != offset+len(bvalue) {
|
||||
panic("incorrect length")
|
||||
}
|
||||
|
||||
return &Component{
|
||||
bytes: maddr,
|
||||
protocol: protocol,
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Package multiaddr provides an implementation of the Multiaddr network
|
||||
address format. Multiaddr emphasizes explicitness, self-description, and
|
||||
portability. It allows applications to treat addresses as opaque tokens,
|
||||
and to avoid making assumptions about the address representation (e.g. length).
|
||||
Learn more at https://github.com/multiformats/multiaddr
|
||||
|
||||
Basic Use:
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// construct from a string (err signals parse failure)
|
||||
m1, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
|
||||
// construct from bytes (err signals parse failure)
|
||||
m2, err := ma.NewMultiaddrBytes(m1.Bytes())
|
||||
|
||||
// true
|
||||
strings.Equal(m1.String(), "/ip4/127.0.0.1/udp/1234")
|
||||
strings.Equal(m1.String(), m2.String())
|
||||
bytes.Equal(m1.Bytes(), m2.Bytes())
|
||||
m1.Equal(m2)
|
||||
m2.Equal(m1)
|
||||
|
||||
// tunneling (en/decap)
|
||||
printer, _ := ma.NewMultiaddr("/ip4/192.168.0.13/tcp/80")
|
||||
proxy, _ := ma.NewMultiaddr("/ip4/10.20.30.40/tcp/443")
|
||||
printerOverProxy := proxy.Encapsulate(printer)
|
||||
proxyAgain := printerOverProxy.Decapsulate(printer)
|
||||
|
||||
*/
|
||||
package multiaddr
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/multiformats/go-multiaddr
|
||||
|
||||
require github.com/multiformats/go-multihash v0.0.1
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
|
||||
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
github.com/multiformats/go-multihash v1.0.10 h1:KUnC6rT8Vyw0gx4qXUS6VN1QHKrgmvdDCaURVQ7+miM=
|
||||
github.com/multiformats/go-multihash v1.0.10/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
/*
|
||||
Multiaddr is a cross-protocol, cross-platform format for representing
|
||||
internet addresses. It emphasizes explicitness and self-description.
|
||||
Learn more here: https://github.com/multiformats/multiaddr
|
||||
|
||||
Multiaddrs have both a binary and string representation.
|
||||
|
||||
import ma "github.com/multiformats/go-multiaddr"
|
||||
|
||||
addr, err := ma.NewMultiaddr("/ip4/1.2.3.4/tcp/80")
|
||||
// err non-nil when parsing failed.
|
||||
|
||||
*/
|
||||
type Multiaddr interface {
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
encoding.TextMarshaler
|
||||
encoding.TextUnmarshaler
|
||||
encoding.BinaryMarshaler
|
||||
encoding.BinaryUnmarshaler
|
||||
|
||||
// Equal returns whether two Multiaddrs are exactly equal
|
||||
Equal(Multiaddr) bool
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
//
|
||||
// This function may expose immutable, internal state. Do not modify.
|
||||
Bytes() []byte
|
||||
|
||||
// String returns the string representation of this Multiaddr
|
||||
// (may panic if internal state is corrupted)
|
||||
String() string
|
||||
|
||||
// Protocols returns the list of Protocols this Multiaddr includes
|
||||
// will panic if protocol code incorrect (and bytes accessed incorrectly)
|
||||
Protocols() []Protocol
|
||||
|
||||
// Encapsulate wraps this Multiaddr around another. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4 encapsulate /tcp/80 = /ip4/1.2.3.4/tcp/80
|
||||
//
|
||||
Encapsulate(Multiaddr) Multiaddr
|
||||
|
||||
// Decapsultate removes a Multiaddr wrapping. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80
|
||||
//
|
||||
Decapsulate(Multiaddr) Multiaddr
|
||||
|
||||
// ValueForProtocol returns the value (if any) following the specified protocol
|
||||
//
|
||||
// Note: protocols can appear multiple times in a single multiaddr.
|
||||
// Consider using `ForEach` to walk over the addr manually.
|
||||
ValueForProtocol(code int) (string, error)
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// multiaddr is the data structure representing a Multiaddr
|
||||
type multiaddr struct {
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
// NewMultiaddr parses and validates an input string, returning a *Multiaddr
|
||||
func NewMultiaddr(s string) (a Multiaddr, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
log.Printf("Panic in NewMultiaddr on input %q: %s", s, e)
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}()
|
||||
b, err := stringToBytes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &multiaddr{bytes: b}, nil
|
||||
}
|
||||
|
||||
// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
|
||||
// It validates it as an input string.
|
||||
func NewMultiaddrBytes(b []byte) (a Multiaddr, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
log.Printf("Panic in NewMultiaddrBytes on input %q: %s", b, e)
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := validateBytes(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &multiaddr{bytes: b}, nil
|
||||
}
|
||||
|
||||
// Equal tests whether two multiaddrs are equal
|
||||
func (m *multiaddr) Equal(m2 Multiaddr) bool {
|
||||
return bytes.Equal(m.bytes, m2.Bytes())
|
||||
}
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
//
|
||||
// Do not modify the returned buffer, it may be shared.
|
||||
func (m *multiaddr) Bytes() []byte {
|
||||
return m.bytes
|
||||
}
|
||||
|
||||
// String returns the string representation of a Multiaddr
|
||||
func (m *multiaddr) String() string {
|
||||
s, err := bytesToString(m.bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("multiaddr failed to convert back to string. corrupted? %s", err))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *multiaddr) MarshalBinary() ([]byte, error) {
|
||||
return m.Bytes(), nil
|
||||
}
|
||||
|
||||
func (m *multiaddr) UnmarshalBinary(data []byte) error {
|
||||
new, err := NewMultiaddrBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = *(new.(*multiaddr))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *multiaddr) MarshalText() ([]byte, error) {
|
||||
return []byte(m.String()), nil
|
||||
}
|
||||
|
||||
func (m *multiaddr) UnmarshalText(data []byte) error {
|
||||
new, err := NewMultiaddr(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = *(new.(*multiaddr))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *multiaddr) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.String())
|
||||
}
|
||||
|
||||
func (m *multiaddr) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
new, err := NewMultiaddr(v)
|
||||
*m = *(new.(*multiaddr))
|
||||
return err
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols this Multiaddr has.
|
||||
// will panic in case we access bytes incorrectly.
|
||||
func (m *multiaddr) Protocols() []Protocol {
|
||||
ps := make([]Protocol, 0, 8)
|
||||
b := m.bytes
|
||||
for len(b) > 0 {
|
||||
code, n, err := ReadVarintCode(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
p := ProtocolWithCode(code)
|
||||
if p.Code == 0 {
|
||||
// this is a panic (and not returning err) because this should've been
|
||||
// caught on constructing the Multiaddr
|
||||
panic(fmt.Errorf("no protocol with code %d", b[0]))
|
||||
}
|
||||
ps = append(ps, p)
|
||||
b = b[n:]
|
||||
|
||||
n, size, err := sizeForAddr(p, b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b = b[n+size:]
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
|
||||
func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
|
||||
mb := m.bytes
|
||||
ob := o.Bytes()
|
||||
|
||||
b := make([]byte, len(mb)+len(ob))
|
||||
copy(b, mb)
|
||||
copy(b[len(mb):], ob)
|
||||
return &multiaddr{bytes: b}
|
||||
}
|
||||
|
||||
// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
|
||||
func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
|
||||
s1 := m.String()
|
||||
s2 := o.String()
|
||||
i := strings.LastIndex(s1, s2)
|
||||
if i < 0 {
|
||||
// if multiaddr not contained, returns a copy.
|
||||
cpy := make([]byte, len(m.bytes))
|
||||
copy(cpy, m.bytes)
|
||||
return &multiaddr{bytes: cpy}
|
||||
}
|
||||
|
||||
ma, err := NewMultiaddr(s1[:i])
|
||||
if err != nil {
|
||||
panic("Multiaddr.Decapsulate incorrect byte boundaries.")
|
||||
}
|
||||
return ma
|
||||
}
|
||||
|
||||
var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr")
|
||||
|
||||
func (m *multiaddr) ValueForProtocol(code int) (value string, err error) {
|
||||
err = ErrProtocolNotFound
|
||||
ForEach(m, func(c Component) bool {
|
||||
if c.Protocol().Code == code {
|
||||
value = c.Value()
|
||||
err = nil
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"author": "multiformats",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multiaddr/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multiaddr"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"hash": "QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW",
|
||||
"name": "go-multihash",
|
||||
"version": "1.0.9"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.9.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "go-multiaddr",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "1.4.1"
|
||||
}
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// These are special sizes
|
||||
const (
|
||||
LengthPrefixedVarSize = -1
|
||||
)
|
||||
|
||||
// Protocol is a Multiaddr protocol description structure.
|
||||
type Protocol struct {
|
||||
// Name is the string representation of the protocol code. E.g., ip4,
|
||||
// ip6, tcp, udp, etc.
|
||||
Name string
|
||||
|
||||
// Code is the protocol's multicodec (a normal, non-varint number).
|
||||
Code int
|
||||
|
||||
// VCode is a precomputed varint encoded version of Code.
|
||||
VCode []byte
|
||||
|
||||
// Size is the size of the argument to this protocol.
|
||||
//
|
||||
// * Size == 0 means this protocol takes no argument.
|
||||
// * Size > 0 means this protocol takes a constant sized argument.
|
||||
// * Size < 0 means this protocol takes a variable length, varint
|
||||
// prefixed argument.
|
||||
Size int // a size of -1 indicates a length-prefixed variable size
|
||||
|
||||
// Path indicates a path protocol (e.g., unix). When parsing multiaddr
|
||||
// strings, path protocols consume the remainder of the address instead
|
||||
// of stopping at the next forward slash.
|
||||
//
|
||||
// Size must be LengthPrefixedVarSize.
|
||||
Path bool
|
||||
|
||||
// Transcoder converts between the byte representation and the string
|
||||
// representation of this protocol's argument (if any).
|
||||
//
|
||||
// This should only be non-nil if Size != 0
|
||||
Transcoder Transcoder
|
||||
}
|
||||
|
||||
var protocolsByName = map[string]Protocol{}
|
||||
var protocolsByCode = map[int]Protocol{}
|
||||
|
||||
// Protocols is the list of multiaddr protocols supported by this module.
|
||||
var Protocols = []Protocol{}
|
||||
|
||||
// SwapToP2pMultiaddrs is a function to make the transition from /ipfs/...
|
||||
// multiaddrs to /p2p/... multiaddrs easier
|
||||
// The first stage of the rollout is to ship this package to all users so
|
||||
// that all users of multiaddr can parse both /ipfs/ and /p2p/ multiaddrs
|
||||
// as the same code (P_P2P). During this stage of the rollout, all addresses
|
||||
// with P_P2P will continue printing as /ipfs/, so that older clients without
|
||||
// the new parsing code won't break.
|
||||
// Once the network has adopted the new parsing code broadly enough, users of
|
||||
// multiaddr can add a call to this method to an init function in their codebase.
|
||||
// This will cause any P_P2P multiaddr to print out as /p2p/ instead of /ipfs/.
|
||||
// Note that the binary serialization of this multiaddr does not change at any
|
||||
// point. This means that this code is not a breaking network change at any point
|
||||
func SwapToP2pMultiaddrs() {
|
||||
for i := range Protocols {
|
||||
if Protocols[i].Code == P_P2P {
|
||||
Protocols[i].Name = "p2p"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
protoP2P.Name = "p2p"
|
||||
|
||||
protocolsByName["ipfs"] = protoP2P
|
||||
protocolsByName["p2p"] = protoP2P
|
||||
protocolsByCode[protoP2P.Code] = protoP2P
|
||||
}
|
||||
|
||||
func AddProtocol(p Protocol) error {
|
||||
if _, ok := protocolsByName[p.Name]; ok {
|
||||
return fmt.Errorf("protocol by the name %q already exists", p.Name)
|
||||
}
|
||||
|
||||
if _, ok := protocolsByCode[p.Code]; ok {
|
||||
return fmt.Errorf("protocol code %d already taken by %q", p.Code, p.Code)
|
||||
}
|
||||
|
||||
if p.Size != 0 && p.Transcoder == nil {
|
||||
return fmt.Errorf("protocols with arguments must define transcoders")
|
||||
}
|
||||
if p.Path && p.Size >= 0 {
|
||||
return fmt.Errorf("path protocols must have variable-length sizes")
|
||||
}
|
||||
|
||||
Protocols = append(Protocols, p)
|
||||
protocolsByName[p.Name] = p
|
||||
protocolsByCode[p.Code] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtocolWithName returns the Protocol description with given string name.
|
||||
func ProtocolWithName(s string) Protocol {
|
||||
return protocolsByName[s]
|
||||
}
|
||||
|
||||
// ProtocolWithCode returns the Protocol description with given protocol code.
|
||||
func ProtocolWithCode(c int) Protocol {
|
||||
return protocolsByCode[c]
|
||||
}
|
||||
|
||||
// ProtocolsWithString returns a slice of protocols matching given string.
|
||||
func ProtocolsWithString(s string) ([]Protocol, error) {
|
||||
s = strings.Trim(s, "/")
|
||||
sp := strings.Split(s, "/")
|
||||
if len(sp) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
t := make([]Protocol, len(sp))
|
||||
for i, name := range sp {
|
||||
p := ProtocolWithName(name)
|
||||
if p.Code == 0 {
|
||||
return nil, fmt.Errorf("no protocol with name: %s", name)
|
||||
}
|
||||
t[i] = p
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
package multiaddr
|
||||
|
||||
// You **MUST** register your multicodecs with
|
||||
// https://github.com/multiformats/multicodec before adding them here.
|
||||
const (
|
||||
P_IP4 = 0x0004
|
||||
P_TCP = 0x0006
|
||||
P_UDP = 0x0111
|
||||
P_DCCP = 0x0021
|
||||
P_IP6 = 0x0029
|
||||
P_IP6ZONE = 0x002A
|
||||
P_QUIC = 0x01CC
|
||||
P_SCTP = 0x0084
|
||||
P_UDT = 0x012D
|
||||
P_UTP = 0x012E
|
||||
P_UNIX = 0x0190
|
||||
P_P2P = 0x01A5
|
||||
P_IPFS = 0x01A5 // alias for backwards compatability
|
||||
P_HTTP = 0x01E0
|
||||
P_HTTPS = 0x01BB
|
||||
P_ONION = 0x01BC // also for backwards compatibility
|
||||
P_ONION3 = 0x01BD
|
||||
P_GARLIC64 = 0x01CA
|
||||
P_P2P_WEBRTC_DIRECT = 0x0114
|
||||
)
|
||||
|
||||
var (
|
||||
protoIP4 = Protocol{
|
||||
Name: "ip4",
|
||||
Code: P_IP4,
|
||||
VCode: CodeToVarint(P_IP4),
|
||||
Size: 32,
|
||||
Path: false,
|
||||
Transcoder: TranscoderIP4,
|
||||
}
|
||||
protoTCP = Protocol{
|
||||
Name: "tcp",
|
||||
Code: P_TCP,
|
||||
VCode: CodeToVarint(P_TCP),
|
||||
Size: 16,
|
||||
Path: false,
|
||||
Transcoder: TranscoderPort,
|
||||
}
|
||||
protoUDP = Protocol{
|
||||
Name: "udp",
|
||||
Code: P_UDP,
|
||||
VCode: CodeToVarint(P_UDP),
|
||||
Size: 16,
|
||||
Path: false,
|
||||
Transcoder: TranscoderPort,
|
||||
}
|
||||
protoDCCP = Protocol{
|
||||
Name: "dccp",
|
||||
Code: P_DCCP,
|
||||
VCode: CodeToVarint(P_DCCP),
|
||||
Size: 16,
|
||||
Path: false,
|
||||
Transcoder: TranscoderPort,
|
||||
}
|
||||
protoIP6 = Protocol{
|
||||
Name: "ip6",
|
||||
Code: P_IP6,
|
||||
VCode: CodeToVarint(P_IP6),
|
||||
Size: 128,
|
||||
Transcoder: TranscoderIP6,
|
||||
}
|
||||
// these require varint
|
||||
protoIP6ZONE = Protocol{
|
||||
Name: "ip6zone",
|
||||
Code: P_IP6ZONE,
|
||||
VCode: CodeToVarint(P_IP6ZONE),
|
||||
Size: LengthPrefixedVarSize,
|
||||
Path: false,
|
||||
Transcoder: TranscoderIP6Zone,
|
||||
}
|
||||
protoSCTP = Protocol{
|
||||
Name: "sctp",
|
||||
Code: P_SCTP,
|
||||
VCode: CodeToVarint(P_SCTP),
|
||||
Size: 16,
|
||||
Transcoder: TranscoderPort,
|
||||
}
|
||||
protoONION2 = Protocol{
|
||||
Name: "onion",
|
||||
Code: P_ONION,
|
||||
VCode: CodeToVarint(P_ONION),
|
||||
Size: 96,
|
||||
Transcoder: TranscoderOnion,
|
||||
}
|
||||
protoONION3 = Protocol{
|
||||
Name: "onion3",
|
||||
Code: P_ONION3,
|
||||
VCode: CodeToVarint(P_ONION3),
|
||||
Size: 296,
|
||||
Transcoder: TranscoderOnion3,
|
||||
}
|
||||
protoGARLIC64 = Protocol{
|
||||
Name: "garlic64",
|
||||
Code: P_GARLIC64,
|
||||
VCode: CodeToVarint(P_GARLIC64),
|
||||
Size: LengthPrefixedVarSize,
|
||||
Transcoder: TranscoderGarlic64,
|
||||
}
|
||||
protoUTP = Protocol{
|
||||
Name: "utp",
|
||||
Code: P_UTP,
|
||||
VCode: CodeToVarint(P_UTP),
|
||||
}
|
||||
protoUDT = Protocol{
|
||||
Name: "udt",
|
||||
Code: P_UDT,
|
||||
VCode: CodeToVarint(P_UDT),
|
||||
}
|
||||
protoQUIC = Protocol{
|
||||
Name: "quic",
|
||||
Code: P_QUIC,
|
||||
VCode: CodeToVarint(P_QUIC),
|
||||
}
|
||||
protoHTTP = Protocol{
|
||||
Name: "http",
|
||||
Code: P_HTTP,
|
||||
VCode: CodeToVarint(P_HTTP),
|
||||
}
|
||||
protoHTTPS = Protocol{
|
||||
Name: "https",
|
||||
Code: P_HTTPS,
|
||||
VCode: CodeToVarint(P_HTTPS),
|
||||
}
|
||||
protoP2P = Protocol{
|
||||
Name: "ipfs",
|
||||
Code: P_P2P,
|
||||
VCode: CodeToVarint(P_P2P),
|
||||
Size: LengthPrefixedVarSize,
|
||||
Transcoder: TranscoderP2P,
|
||||
}
|
||||
protoUNIX = Protocol{
|
||||
Name: "unix",
|
||||
Code: P_UNIX,
|
||||
VCode: CodeToVarint(P_UNIX),
|
||||
Size: LengthPrefixedVarSize,
|
||||
Path: true,
|
||||
Transcoder: TranscoderUnix,
|
||||
}
|
||||
protoP2P_WEBRTC_DIRECT = Protocol{
|
||||
Name: "p2p-webrtc-direct",
|
||||
Code: P_P2P_WEBRTC_DIRECT,
|
||||
VCode: CodeToVarint(P_P2P_WEBRTC_DIRECT),
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, p := range []Protocol{
|
||||
protoIP4,
|
||||
protoTCP,
|
||||
protoUDP,
|
||||
protoDCCP,
|
||||
protoIP6,
|
||||
protoIP6ZONE,
|
||||
protoSCTP,
|
||||
protoONION2,
|
||||
protoONION3,
|
||||
protoGARLIC64,
|
||||
protoUTP,
|
||||
protoUDT,
|
||||
protoQUIC,
|
||||
protoHTTP,
|
||||
protoHTTPS,
|
||||
protoP2P,
|
||||
protoUNIX,
|
||||
protoP2P_WEBRTC_DIRECT,
|
||||
} {
|
||||
if err := AddProtocol(p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// explicitly set both of these
|
||||
protocolsByName["p2p"] = protoP2P
|
||||
protocolsByName["ipfs"] = protoP2P
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
type Transcoder interface {
|
||||
StringToBytes(string) ([]byte, error)
|
||||
BytesToString([]byte) (string, error)
|
||||
ValidateBytes([]byte) error
|
||||
}
|
||||
|
||||
func NewTranscoderFromFunctions(
|
||||
s2b func(string) ([]byte, error),
|
||||
b2s func([]byte) (string, error),
|
||||
val func([]byte) error,
|
||||
) Transcoder {
|
||||
return twrp{s2b, b2s, val}
|
||||
}
|
||||
|
||||
type twrp struct {
|
||||
strtobyte func(string) ([]byte, error)
|
||||
bytetostr func([]byte) (string, error)
|
||||
validbyte func([]byte) error
|
||||
}
|
||||
|
||||
func (t twrp) StringToBytes(s string) ([]byte, error) {
|
||||
return t.strtobyte(s)
|
||||
}
|
||||
func (t twrp) BytesToString(b []byte) (string, error) {
|
||||
return t.bytetostr(b)
|
||||
}
|
||||
|
||||
func (t twrp) ValidateBytes(b []byte) error {
|
||||
if t.validbyte == nil {
|
||||
return nil
|
||||
}
|
||||
return t.validbyte(b)
|
||||
}
|
||||
|
||||
var TranscoderIP4 = NewTranscoderFromFunctions(ip4StB, ip4BtS, nil)
|
||||
var TranscoderIP6 = NewTranscoderFromFunctions(ip6StB, ip6BtS, nil)
|
||||
var TranscoderIP6Zone = NewTranscoderFromFunctions(ip6zoneStB, ip6zoneBtS, ip6zoneVal)
|
||||
|
||||
func ip4StB(s string) ([]byte, error) {
|
||||
i := net.ParseIP(s).To4()
|
||||
if i == nil {
|
||||
return nil, fmt.Errorf("failed to parse ip4 addr: %s", s)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func ip6zoneStB(s string) ([]byte, error) {
|
||||
if len(s) == 0 {
|
||||
return nil, fmt.Errorf("empty ip6zone")
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
func ip6zoneBtS(b []byte) (string, error) {
|
||||
if len(b) == 0 {
|
||||
return "", fmt.Errorf("invalid length (should be > 0)")
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func ip6zoneVal(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
return fmt.Errorf("invalid length (should be > 0)")
|
||||
}
|
||||
// Not supported as this would break multiaddrs.
|
||||
if bytes.IndexByte(b, '/') >= 0 {
|
||||
return fmt.Errorf("IPv6 zone ID contains '/': %s", string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ip6StB(s string) ([]byte, error) {
|
||||
i := net.ParseIP(s).To16()
|
||||
if i == nil {
|
||||
return nil, fmt.Errorf("failed to parse ip6 addr: %s", s)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func ip6BtS(b []byte) (string, error) {
|
||||
ip := net.IP(b)
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
// Go fails to prepend the `::ffff:` part.
|
||||
return "::ffff:" + ip4.String(), nil
|
||||
}
|
||||
return ip.String(), nil
|
||||
}
|
||||
|
||||
func ip4BtS(b []byte) (string, error) {
|
||||
return net.IP(b).String(), nil
|
||||
}
|
||||
|
||||
var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil)
|
||||
|
||||
func portStB(s string) ([]byte, error) {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse port addr: %s", err)
|
||||
}
|
||||
if i >= 65536 {
|
||||
return nil, fmt.Errorf("failed to parse port addr: %s", "greater than 65536")
|
||||
}
|
||||
b := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(b, uint16(i))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func portBtS(b []byte) (string, error) {
|
||||
i := binary.BigEndian.Uint16(b)
|
||||
return strconv.Itoa(int(i)), nil
|
||||
}
|
||||
|
||||
var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, nil)
|
||||
|
||||
func onionStB(s string) ([]byte, error) {
|
||||
addr := strings.Split(s, ":")
|
||||
if len(addr) != 2 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number.", s)
|
||||
}
|
||||
|
||||
// onion address without the ".onion" substring
|
||||
if len(addr[0]) != 16 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onion address.", s)
|
||||
}
|
||||
onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err)
|
||||
}
|
||||
|
||||
// onion port number
|
||||
i, err := strconv.Atoi(addr[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", err)
|
||||
}
|
||||
if i >= 65536 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536")
|
||||
}
|
||||
if i < 1 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1")
|
||||
}
|
||||
|
||||
onionPortBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
|
||||
bytes := []byte{}
|
||||
bytes = append(bytes, onionHostBytes...)
|
||||
bytes = append(bytes, onionPortBytes...)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func onionBtS(b []byte) (string, error) {
|
||||
addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
|
||||
port := binary.BigEndian.Uint16(b[10:12])
|
||||
return addr + ":" + strconv.Itoa(int(port)), nil
|
||||
}
|
||||
|
||||
var TranscoderOnion3 = NewTranscoderFromFunctions(onion3StB, onion3BtS, nil)
|
||||
|
||||
func onion3StB(s string) ([]byte, error) {
|
||||
addr := strings.Split(s, ":")
|
||||
if len(addr) != 2 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number.", s)
|
||||
}
|
||||
|
||||
// onion address without the ".onion" substring
|
||||
if len(addr[0]) != 56 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onionv3 address. len == %d", s, len(addr[0]))
|
||||
}
|
||||
onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err)
|
||||
}
|
||||
|
||||
// onion port number
|
||||
i, err := strconv.Atoi(addr[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", err)
|
||||
}
|
||||
if i >= 65536 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536")
|
||||
}
|
||||
if i < 1 {
|
||||
return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1")
|
||||
}
|
||||
|
||||
onionPortBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
|
||||
bytes := []byte{}
|
||||
bytes = append(bytes, onionHostBytes[0:35]...)
|
||||
bytes = append(bytes, onionPortBytes...)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func onion3BtS(b []byte) (string, error) {
|
||||
addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:35]))
|
||||
port := binary.BigEndian.Uint16(b[35:37])
|
||||
str := addr + ":" + strconv.Itoa(int(port))
|
||||
return str, nil
|
||||
}
|
||||
|
||||
var TranscoderGarlic64 = NewTranscoderFromFunctions(garlic64StB, garlic64BtS, garlicValidate)
|
||||
|
||||
// i2p uses an alternate character set for base64 addresses. This returns an appropriate encoder.
|
||||
var garlicBase64Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
|
||||
|
||||
func garlic64StB(s string) ([]byte, error) {
|
||||
// i2p base64 address
|
||||
if len(s) < 516 || len(s) > 616 {
|
||||
return nil, fmt.Errorf("failed to parse garlic addr: %s not an i2p base64 address. len: %d\n", s, len(s))
|
||||
}
|
||||
garlicHostBytes, err := garlicBase64Encoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base64 i2p addr: %s %s", s, err)
|
||||
}
|
||||
|
||||
return garlicHostBytes, nil
|
||||
}
|
||||
|
||||
func garlic64BtS(b []byte) (string, error) {
|
||||
if len(b) < 386 {
|
||||
return "", fmt.Errorf("failed to validate garlic addr: %s not an i2p base64 address. len: %d\n", b, len(b))
|
||||
}
|
||||
addr := garlicBase64Encoding.EncodeToString(b)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func garlicValidate(b []byte) error {
|
||||
if len(b) < 386 {
|
||||
return fmt.Errorf("failed to validate garlic addr: %s not an i2p base64 address. len: %d\n", b, len(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var TranscoderP2P = NewTranscoderFromFunctions(p2pStB, p2pBtS, p2pVal)
|
||||
|
||||
func p2pStB(s string) ([]byte, error) {
|
||||
// the address is a varint prefixed multihash string representation
|
||||
m, err := mh.FromB58String(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse p2p addr: %s %s", s, err)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func p2pVal(b []byte) error {
|
||||
_, err := mh.Cast(b)
|
||||
return err
|
||||
}
|
||||
|
||||
func p2pBtS(b []byte) (string, error) {
|
||||
m, err := mh.Cast(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return m.B58String(), nil
|
||||
}
|
||||
|
||||
var TranscoderUnix = NewTranscoderFromFunctions(unixStB, unixBtS, nil)
|
||||
|
||||
func unixStB(s string) ([]byte, error) {
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
func unixBtS(b []byte) (string, error) {
|
||||
return string(b), nil
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
package multiaddr
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Split returns the sub-address portions of a multiaddr.
|
||||
func Split(m Multiaddr) []Multiaddr {
|
||||
if _, ok := m.(*Component); ok {
|
||||
return []Multiaddr{m}
|
||||
}
|
||||
var addrs []Multiaddr
|
||||
ForEach(m, func(c Component) bool {
|
||||
addrs = append(addrs, &c)
|
||||
return true
|
||||
})
|
||||
return addrs
|
||||
}
|
||||
|
||||
// Join returns a combination of addresses.
|
||||
func Join(ms ...Multiaddr) Multiaddr {
|
||||
switch len(ms) {
|
||||
case 0:
|
||||
// empty multiaddr, unfortunately, we have callers that rely on
|
||||
// this contract.
|
||||
return &multiaddr{}
|
||||
case 1:
|
||||
return ms[0]
|
||||
}
|
||||
|
||||
length := 0
|
||||
bs := make([][]byte, len(ms))
|
||||
for i, m := range ms {
|
||||
bs[i] = m.Bytes()
|
||||
length += len(bs[i])
|
||||
}
|
||||
|
||||
bidx := 0
|
||||
b := make([]byte, length)
|
||||
for _, mb := range bs {
|
||||
bidx += copy(b[bidx:], mb)
|
||||
}
|
||||
return &multiaddr{bytes: b}
|
||||
}
|
||||
|
||||
// Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse.
|
||||
func Cast(b []byte) Multiaddr {
|
||||
m, err := NewMultiaddrBytes(b)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("multiaddr failed to parse: %s", err))
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// StringCast like Cast, but parses a string. Will also panic if it fails to parse.
|
||||
func StringCast(s string) Multiaddr {
|
||||
m, err := NewMultiaddr(s)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("multiaddr failed to parse: %s", err))
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// SplitFirst returns the first component and the rest of the multiaddr.
|
||||
func SplitFirst(m Multiaddr) (*Component, Multiaddr) {
|
||||
// Shortcut if we already have a component
|
||||
if c, ok := m.(*Component); ok {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
b := m.Bytes()
|
||||
if len(b) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
n, c, err := readComponent(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(b) == n {
|
||||
return &c, nil
|
||||
}
|
||||
return &c, &multiaddr{b[n:]}
|
||||
}
|
||||
|
||||
// SplitLast returns the rest of the multiaddr and the last component.
|
||||
func SplitLast(m Multiaddr) (Multiaddr, *Component) {
|
||||
// Shortcut if we already have a component
|
||||
if c, ok := m.(*Component); ok {
|
||||
return nil, c
|
||||
}
|
||||
|
||||
b := m.Bytes()
|
||||
if len(b) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
c Component
|
||||
err error
|
||||
offset int
|
||||
)
|
||||
for {
|
||||
var n int
|
||||
n, c, err = readComponent(b[offset:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(b) == n+offset {
|
||||
// Reached end
|
||||
if offset == 0 {
|
||||
// Only one component
|
||||
return nil, &c
|
||||
}
|
||||
return &multiaddr{b[:offset]}, &c
|
||||
}
|
||||
offset += n
|
||||
}
|
||||
}
|
||||
|
||||
// SplitFunc splits the multiaddr when the callback first returns true. The
|
||||
// component on which the callback first returns will be included in the
|
||||
// *second* multiaddr.
|
||||
func SplitFunc(m Multiaddr, cb func(Component) bool) (Multiaddr, Multiaddr) {
|
||||
// Shortcut if we already have a component
|
||||
if c, ok := m.(*Component); ok {
|
||||
if cb(*c) {
|
||||
return nil, m
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
b := m.Bytes()
|
||||
if len(b) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
c Component
|
||||
err error
|
||||
offset int
|
||||
)
|
||||
for offset < len(b) {
|
||||
var n int
|
||||
n, c, err = readComponent(b[offset:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if cb(c) {
|
||||
break
|
||||
}
|
||||
offset += n
|
||||
}
|
||||
switch offset {
|
||||
case 0:
|
||||
return nil, m
|
||||
case len(b):
|
||||
return m, nil
|
||||
default:
|
||||
return &multiaddr{b[:offset]}, &multiaddr{b[offset:]}
|
||||
}
|
||||
}
|
||||
|
||||
// ForEach walks over the multiaddr, component by component.
|
||||
//
|
||||
// This function iterates over components *by value* to avoid allocating.
|
||||
func ForEach(m Multiaddr, cb func(c Component) bool) {
|
||||
// Shortcut if we already have a component
|
||||
if c, ok := m.(*Component); ok {
|
||||
cb(*c)
|
||||
return
|
||||
}
|
||||
|
||||
b := m.Bytes()
|
||||
for len(b) > 0 {
|
||||
n, c, err := readComponent(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !cb(c) {
|
||||
return
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// VarintSize returns the size (in bytes) of `num` encoded as a varint.
|
||||
func VarintSize(num int) int {
|
||||
bits := bits.Len(uint(num))
|
||||
q, r := bits/7, bits%7
|
||||
size := q
|
||||
if r > 0 || size == 0 {
|
||||
size++
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// CodeToVarint converts an integer to a varint-encoded []byte
|
||||
func CodeToVarint(num int) []byte {
|
||||
buf := make([]byte, VarintSize(num))
|
||||
n := binary.PutUvarint(buf, uint64(num))
|
||||
return buf[:n]
|
||||
}
|
||||
|
||||
// VarintToCode converts a varint-encoded []byte to an integer protocol code
|
||||
func VarintToCode(buf []byte) int {
|
||||
num, _, err := ReadVarintCode(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
// ReadVarintCode reads a varint code from the beginning of buf.
|
||||
// returns the code, and the number of bytes read.
|
||||
func ReadVarintCode(buf []byte) (int, int, error) {
|
||||
num, n := binary.Uvarint(buf)
|
||||
if n < 0 {
|
||||
return 0, 0, fmt.Errorf("varints larger than uint64 not yet supported")
|
||||
}
|
||||
return int(num), n, nil
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
comment: off
|
||||
@@ -0,0 +1,3 @@
|
||||
*.swp
|
||||
|
||||
multibase-conv/multibase-conv
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "spec"]
|
||||
path = spec
|
||||
url = https://github.com/multiformats/multibase.git
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
/spec/
|
||||
*_test.go
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Protocol Labs Inc.
|
||||
|
||||
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.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
test: deps
|
||||
go test -race -v ./...
|
||||
|
||||
export IPFS_API ?= v04x.ipfs.io
|
||||
|
||||
gx:
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
go get -t ./...
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# go-multibase
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://travis-ci.org/multiformats/go-multibase)
|
||||
[](https://codecov.io/github/multiformats/go-multibase?branch=master)
|
||||
|
||||
> Implementation of [multibase](https://github.com/multiformats/multibase) -self identifying base encodings- in Go.
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
`go-multibase` is a standard Go module which can be installed with:
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multibase
|
||||
```
|
||||
|
||||
Note that `go-multibase` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
|
||||
|
||||
## Usage
|
||||
|
||||
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project it is recommended that you:
|
||||
|
||||
```sh
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
cd <your-project-repository>
|
||||
gx init
|
||||
gx import github.com/multiformats/go-multibase
|
||||
gx install --global
|
||||
gx-go --rewrite
|
||||
```
|
||||
|
||||
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multibase/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2016 Protocol Labs Inc.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package multibase
|
||||
|
||||
func hexEncodeToStringUpper(src []byte) string {
|
||||
dst := make([]byte, len(src)*2)
|
||||
hexEncodeUpper(dst, src)
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
var hexTableUppers = [16]byte{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F',
|
||||
}
|
||||
|
||||
func hexEncodeUpper(dst, src []byte) int {
|
||||
for i, v := range src {
|
||||
dst[i*2] = hexTableUppers[v>>4]
|
||||
dst[i*2+1] = hexTableUppers[v&0x0f]
|
||||
}
|
||||
|
||||
return len(src) * 2
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package multibase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// binaryEncodeToString takes an array of bytes and returns
|
||||
// multibase binary representation
|
||||
func binaryEncodeToString(src []byte) string {
|
||||
dst := make([]byte, len(src)*8)
|
||||
encodeBinary(dst, src)
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
// encodeBinary takes the src and dst bytes and converts each
|
||||
// byte to their binary rep using power reduction method
|
||||
func encodeBinary(dst []byte, src []byte) {
|
||||
for i, b := range src {
|
||||
for j := 0; j < 8; j++ {
|
||||
if b&(1<<uint(7-j)) == 0 {
|
||||
dst[i*8+j] = '0'
|
||||
} else {
|
||||
dst[i*8+j] = '1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decodeBinaryString takes multibase binary representation
|
||||
// and returns a byte array
|
||||
func decodeBinaryString(s string) ([]byte, error) {
|
||||
if len(s)&7 != 0 {
|
||||
// prepend the padding
|
||||
s = strings.Repeat("0", 8-len(s)&7) + s
|
||||
}
|
||||
|
||||
data := make([]byte, len(s)>>3)
|
||||
|
||||
for i, dstIndex := 0, 0; i < len(s); i = i + 8 {
|
||||
value, err := strconv.ParseInt(s[i:i+8], 2, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while conversion: %s", err)
|
||||
}
|
||||
|
||||
data[dstIndex] = byte(value)
|
||||
dstIndex++
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package multibase
|
||||
|
||||
import (
|
||||
b32 "github.com/multiformats/go-base32"
|
||||
)
|
||||
|
||||
var base32StdLowerPad = b32.NewEncodingCI("abcdefghijklmnopqrstuvwxyz234567")
|
||||
var base32StdLowerNoPad = base32StdLowerPad.WithPadding(b32.NoPadding)
|
||||
|
||||
var base32StdUpperPad = b32.NewEncodingCI("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
|
||||
var base32StdUpperNoPad = base32StdUpperPad.WithPadding(b32.NoPadding)
|
||||
|
||||
var base32HexLowerPad = b32.NewEncodingCI("0123456789abcdefghijklmnopqrstuv")
|
||||
var base32HexLowerNoPad = base32HexLowerPad.WithPadding(b32.NoPadding)
|
||||
|
||||
var base32HexUpperPad = b32.NewEncodingCI("0123456789ABCDEFGHIJKLMNOPQRSTUV")
|
||||
var base32HexUpperNoPad = base32HexUpperPad.WithPadding(b32.NoPadding)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package multibase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Encoder is a multibase encoding that is verified to be supported and
|
||||
// supports an Encode method that does not return an error
|
||||
type Encoder struct {
|
||||
enc Encoding
|
||||
}
|
||||
|
||||
// NewEncoder create a new Encoder from an Encoding
|
||||
func NewEncoder(base Encoding) (Encoder, error) {
|
||||
_, ok := EncodingToStr[base]
|
||||
if !ok {
|
||||
return Encoder{-1}, fmt.Errorf("Unsupported multibase encoding: %d", base)
|
||||
}
|
||||
return Encoder{base}, nil
|
||||
}
|
||||
|
||||
// MustNewEncoder is like NewEncoder but will panic if the encoding is
|
||||
// invalid.
|
||||
func MustNewEncoder(base Encoding) Encoder {
|
||||
_, ok := EncodingToStr[base]
|
||||
if !ok {
|
||||
panic("Unsupported multibase encoding")
|
||||
}
|
||||
return Encoder{base}
|
||||
}
|
||||
|
||||
// EncoderByName creates an encoder from a string, the string can
|
||||
// either be the multibase name or single character multibase prefix
|
||||
func EncoderByName(str string) (Encoder, error) {
|
||||
var base Encoding
|
||||
ok := true
|
||||
if len(str) == 0 {
|
||||
return Encoder{-1}, fmt.Errorf("Empty multibase encoding")
|
||||
} else if len(str) == 1 {
|
||||
base = Encoding(str[0])
|
||||
_, ok = EncodingToStr[base]
|
||||
} else {
|
||||
base, ok = Encodings[str]
|
||||
}
|
||||
if !ok {
|
||||
return Encoder{-1}, fmt.Errorf("Unsupported multibase encoding: %s", str)
|
||||
}
|
||||
return Encoder{base}, nil
|
||||
}
|
||||
|
||||
func (p Encoder) Encoding() Encoding {
|
||||
return p.enc
|
||||
}
|
||||
|
||||
// Encode encodes the multibase using the given Encoder.
|
||||
func (p Encoder) Encode(data []byte) string {
|
||||
str, err := Encode(p.enc, data)
|
||||
if err != nil {
|
||||
// should not happen
|
||||
panic(err)
|
||||
}
|
||||
return str
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
module github.com/multiformats/go-multibase
|
||||
|
||||
require (
|
||||
github.com/mr-tron/base58 v1.1.0
|
||||
github.com/multiformats/go-base32 v0.0.3
|
||||
)
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
|
||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package multibase
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
b58 "github.com/mr-tron/base58/base58"
|
||||
b32 "github.com/multiformats/go-base32"
|
||||
)
|
||||
|
||||
// Encoding identifies the type of base-encoding that a multibase is carrying.
|
||||
type Encoding int
|
||||
|
||||
// These are the encodings specified in the standard, not are all
|
||||
// supported yet
|
||||
const (
|
||||
Identity = 0x00
|
||||
Base1 = '1'
|
||||
Base2 = '0'
|
||||
Base8 = '7'
|
||||
Base10 = '9'
|
||||
Base16 = 'f'
|
||||
Base16Upper = 'F'
|
||||
Base32 = 'b'
|
||||
Base32Upper = 'B'
|
||||
Base32pad = 'c'
|
||||
Base32padUpper = 'C'
|
||||
Base32hex = 'v'
|
||||
Base32hexUpper = 'V'
|
||||
Base32hexPad = 't'
|
||||
Base32hexPadUpper = 'T'
|
||||
Base58Flickr = 'Z'
|
||||
Base58BTC = 'z'
|
||||
Base64 = 'm'
|
||||
Base64url = 'u'
|
||||
Base64pad = 'M'
|
||||
Base64urlPad = 'U'
|
||||
)
|
||||
|
||||
// Encodings is a map of the supported encoding, unsupported encoding
|
||||
// specified in standard are left out
|
||||
var Encodings = map[string]Encoding{
|
||||
"identity": 0x00,
|
||||
"base2": '0',
|
||||
"base16": 'f',
|
||||
"base16upper": 'F',
|
||||
"base32": 'b',
|
||||
"base32upper": 'B',
|
||||
"base32pad": 'c',
|
||||
"base32padupper": 'C',
|
||||
"base32hex": 'v',
|
||||
"base32hexupper": 'V',
|
||||
"base32hexpad": 't',
|
||||
"base32hexpadupper": 'T',
|
||||
"base58flickr": 'Z',
|
||||
"base58btc": 'z',
|
||||
"base64": 'm',
|
||||
"base64url": 'u',
|
||||
"base64pad": 'M',
|
||||
"base64urlpad": 'U',
|
||||
}
|
||||
|
||||
var EncodingToStr = map[Encoding]string{
|
||||
0x00: "identity",
|
||||
'0': "base2",
|
||||
'f': "base16",
|
||||
'F': "base16upper",
|
||||
'b': "base32",
|
||||
'B': "base32upper",
|
||||
'c': "base32pad",
|
||||
'C': "base32padupper",
|
||||
'v': "base32hex",
|
||||
'V': "base32hexupper",
|
||||
't': "base32hexpad",
|
||||
'T': "base32hexpadupper",
|
||||
'Z': "base58flickr",
|
||||
'z': "base58btc",
|
||||
'm': "base64",
|
||||
'u': "base64url",
|
||||
'M': "base64pad",
|
||||
'U': "base64urlpad",
|
||||
}
|
||||
|
||||
// ErrUnsupportedEncoding is returned when the selected encoding is not known or
|
||||
// implemented.
|
||||
var ErrUnsupportedEncoding = fmt.Errorf("selected encoding not supported")
|
||||
|
||||
// Encode encodes a given byte slice with the selected encoding and returns a
|
||||
// multibase string (<encoding><base-encoded-string>). It will return
|
||||
// an error if the selected base is not known.
|
||||
func Encode(base Encoding, data []byte) (string, error) {
|
||||
switch base {
|
||||
case Identity:
|
||||
// 0x00 inside a string is OK in golang and causes no problems with the length calculation.
|
||||
return string(Identity) + string(data), nil
|
||||
case Base2:
|
||||
return string(Base2) + binaryEncodeToString(data), nil
|
||||
case Base16:
|
||||
return string(Base16) + hex.EncodeToString(data), nil
|
||||
case Base16Upper:
|
||||
return string(Base16Upper) + hexEncodeToStringUpper(data), nil
|
||||
case Base32:
|
||||
return string(Base32) + base32StdLowerNoPad.EncodeToString(data), nil
|
||||
case Base32Upper:
|
||||
return string(Base32Upper) + base32StdUpperNoPad.EncodeToString(data), nil
|
||||
case Base32hex:
|
||||
return string(Base32hex) + base32HexLowerNoPad.EncodeToString(data), nil
|
||||
case Base32hexUpper:
|
||||
return string(Base32hexUpper) + base32HexUpperNoPad.EncodeToString(data), nil
|
||||
case Base32pad:
|
||||
return string(Base32pad) + base32StdLowerPad.EncodeToString(data), nil
|
||||
case Base32padUpper:
|
||||
return string(Base32padUpper) + base32StdUpperPad.EncodeToString(data), nil
|
||||
case Base32hexPad:
|
||||
return string(Base32hexPad) + base32HexLowerPad.EncodeToString(data), nil
|
||||
case Base32hexPadUpper:
|
||||
return string(Base32hexPadUpper) + base32HexUpperPad.EncodeToString(data), nil
|
||||
case Base58BTC:
|
||||
return string(Base58BTC) + b58.EncodeAlphabet(data, b58.BTCAlphabet), nil
|
||||
case Base58Flickr:
|
||||
return string(Base58Flickr) + b58.EncodeAlphabet(data, b58.FlickrAlphabet), nil
|
||||
case Base64pad:
|
||||
return string(Base64pad) + base64.StdEncoding.EncodeToString(data), nil
|
||||
case Base64urlPad:
|
||||
return string(Base64urlPad) + base64.URLEncoding.EncodeToString(data), nil
|
||||
case Base64url:
|
||||
return string(Base64url) + base64.RawURLEncoding.EncodeToString(data), nil
|
||||
case Base64:
|
||||
return string(Base64) + base64.RawStdEncoding.EncodeToString(data), nil
|
||||
default:
|
||||
return "", ErrUnsupportedEncoding
|
||||
}
|
||||
}
|
||||
|
||||
// Decode takes a multibase string and decodes into a bytes buffer.
|
||||
// It will return an error if the selected base is not known.
|
||||
func Decode(data string) (Encoding, []byte, error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, fmt.Errorf("cannot decode multibase for zero length string")
|
||||
}
|
||||
|
||||
enc := Encoding(data[0])
|
||||
|
||||
switch enc {
|
||||
case Identity:
|
||||
return Identity, []byte(data[1:]), nil
|
||||
case Base2:
|
||||
bytes, err := decodeBinaryString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base16, Base16Upper:
|
||||
bytes, err := hex.DecodeString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base32, Base32Upper:
|
||||
bytes, err := b32.RawStdEncoding.DecodeString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base32hex, Base32hexUpper:
|
||||
bytes, err := b32.RawHexEncoding.DecodeString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base32pad, Base32padUpper:
|
||||
bytes, err := b32.StdEncoding.DecodeString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base32hexPad, Base32hexPadUpper:
|
||||
bytes, err := b32.HexEncoding.DecodeString(data[1:])
|
||||
return enc, bytes, err
|
||||
case Base58BTC:
|
||||
bytes, err := b58.DecodeAlphabet(data[1:], b58.BTCAlphabet)
|
||||
return Base58BTC, bytes, err
|
||||
case Base58Flickr:
|
||||
bytes, err := b58.DecodeAlphabet(data[1:], b58.FlickrAlphabet)
|
||||
return Base58Flickr, bytes, err
|
||||
case Base64pad:
|
||||
bytes, err := base64.StdEncoding.DecodeString(data[1:])
|
||||
return Base64pad, bytes, err
|
||||
case Base64urlPad:
|
||||
bytes, err := base64.URLEncoding.DecodeString(data[1:])
|
||||
return Base64urlPad, bytes, err
|
||||
case Base64:
|
||||
bytes, err := base64.RawStdEncoding.DecodeString(data[1:])
|
||||
return Base64, bytes, err
|
||||
case Base64url:
|
||||
bytes, err := base64.RawURLEncoding.DecodeString(data[1:])
|
||||
return Base64url, bytes, err
|
||||
default:
|
||||
return -1, nil, ErrUnsupportedEncoding
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multibase"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multibase"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "mr-tron",
|
||||
"hash": "QmWFAMPqsEyUX7gDUsRVmMWz59FxSpJ1b2v6bJ1yYzo7jY",
|
||||
"name": "go-base58-fast",
|
||||
"version": "0.1.1"
|
||||
},
|
||||
{
|
||||
"author": "Golang",
|
||||
"hash": "QmPbbYin7KBd1Y1BfUe15vHzwJiioyi3wtKQTtXWWf8SC5",
|
||||
"name": "base32",
|
||||
"version": "0.0.3"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.8.0",
|
||||
"language": "go",
|
||||
"license": "",
|
||||
"name": "go-multibase",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "0.3.0"
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
|
||||
install:
|
||||
- make deps
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
sudo: false #docker containers for CI
|
||||
|
||||
env: GOTFLAGS="-race -cpu 5"
|
||||
+21
@@ -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.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
gx:
|
||||
go get github.com/whyrusleeping/gx
|
||||
go get github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
|
||||
publish:
|
||||
gx-go rewrite --undo
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# go-multicodec
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://travis-ci.org/multiformats/go-multicodec)
|
||||
[](https://codecov.io/github/multiformats/go-multicodec?branch=master)
|
||||
[](https://godoc.org/github.com/multiformats/go-multicodec)
|
||||
|
||||
> [multicodec](https://github.com/multiformats/multicodec) implementation in Go.
|
||||
|
||||
### Supported codecs
|
||||
|
||||
- `/cbor`
|
||||
- `/json`
|
||||
- `/msgio`
|
||||
- `/msgpack`
|
||||
- `/protobuf`
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multicodec
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Look at the Godocs:
|
||||
|
||||
- https://godoc.org/github.com/multiformats/go-multicodec
|
||||
|
||||
```go
|
||||
import (
|
||||
"os"
|
||||
"io"
|
||||
|
||||
cbor "github.com/multiformats/go-multicodec/cbor"
|
||||
json "github.com/multiformats/go-multicodec/json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dec := cbor.Multicodec().Decoder(os.Stdin)
|
||||
enc := json.Multicodec(false).Encoder(os.Stdout)
|
||||
|
||||
for {
|
||||
var item interface{}
|
||||
|
||||
if err := dec.Decode(&item); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := enc.Encode(&item); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
Captain: [@jbenet](https://github.com/jbenet).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multicodec/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2014 Juan Batiz-Benet
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package b64
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
base "github.com/multiformats/go-multicodec/base"
|
||||
)
|
||||
|
||||
var (
|
||||
HeaderPath = "/base64/"
|
||||
Header = mc.Header([]byte(HeaderPath))
|
||||
multic = mc.NewMulticodecFromCodec(Codec(), Header)
|
||||
)
|
||||
|
||||
type codec struct{}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (d decoder) Decode(v interface{}) error {
|
||||
out, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
|
||||
_, err := d.r.Read(out)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Decoder(r io.Reader) mc.Decoder {
|
||||
return decoder{base64.NewDecoder(base64.StdEncoding, r)}
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.WriteCloser
|
||||
}
|
||||
|
||||
func (e encoder) Encode(v interface{}) error {
|
||||
slice, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
_, err := e.w.Write(slice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.w.Close()
|
||||
}
|
||||
|
||||
func (codec) Encoder(w io.Writer) mc.Encoder {
|
||||
return encoder{base64.NewEncoder(base64.StdEncoding, w)}
|
||||
}
|
||||
|
||||
func Codec() mc.Codec {
|
||||
return codec{}
|
||||
}
|
||||
|
||||
func Multicodec() mc.Multicodec {
|
||||
return multic
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package bin
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
base "github.com/multiformats/go-multicodec/base"
|
||||
)
|
||||
|
||||
var (
|
||||
HeaderPath = "/bin/"
|
||||
Header = mc.Header([]byte(HeaderPath))
|
||||
multic = mc.NewMulticodecFromCodec(Codec(), Header)
|
||||
)
|
||||
|
||||
type codec struct{}
|
||||
|
||||
func (codec) Header() []byte {
|
||||
return Header
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (d decoder) Decode(v interface{}) error {
|
||||
slice, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
|
||||
_, err := d.r.Read(slice)
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Decoder(r io.Reader) mc.Decoder {
|
||||
return decoder{r}
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (e encoder) Encode(v interface{}) error {
|
||||
slice, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
|
||||
_, err := e.w.Write(slice)
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Encoder(w io.Writer) mc.Encoder {
|
||||
return encoder{w}
|
||||
}
|
||||
|
||||
func Codec() mc.Codec {
|
||||
return codec{}
|
||||
}
|
||||
|
||||
func Multicodec() mc.Multicodec {
|
||||
return multic
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package base
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrExpectedByteSlice = errors.New("expected []byte as input")
|
||||
)
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package bin
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
base "github.com/multiformats/go-multicodec/base"
|
||||
)
|
||||
|
||||
var (
|
||||
HeaderPath = "/base16/"
|
||||
Header = mc.Header([]byte(HeaderPath))
|
||||
multic = mc.NewMulticodecFromCodec(Codec(), Header)
|
||||
)
|
||||
|
||||
type codec struct{}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (d decoder) Decode(v interface{}) error {
|
||||
out, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
|
||||
buf := make([]byte, hex.EncodedLen(len(out)))
|
||||
_, err := d.r.Read(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = hex.Decode(out, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Decoder(r io.Reader) mc.Decoder {
|
||||
return decoder{r}
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (e encoder) Encode(v interface{}) error {
|
||||
slice, ok := v.([]byte)
|
||||
if !ok {
|
||||
return base.ErrExpectedByteSlice
|
||||
}
|
||||
|
||||
buf := make([]byte, hex.EncodedLen(len(slice)))
|
||||
hex.Encode(buf, slice)
|
||||
|
||||
_, err := e.w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func (codec) Encoder(w io.Writer) mc.Encoder {
|
||||
return encoder{w}
|
||||
}
|
||||
|
||||
func Codec() mc.Codec {
|
||||
return codec{}
|
||||
}
|
||||
|
||||
func Multicodec() mc.Multicodec {
|
||||
return multic
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package basemux
|
||||
|
||||
import (
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
mux "github.com/multiformats/go-multicodec/mux"
|
||||
|
||||
b64 "github.com/multiformats/go-multicodec/base/b64"
|
||||
bin "github.com/multiformats/go-multicodec/base/bin"
|
||||
hex "github.com/multiformats/go-multicodec/base/hex"
|
||||
)
|
||||
|
||||
func AllBasesMux() *mux.Multicodec {
|
||||
m := mux.MuxMulticodec([]mc.Multicodec{
|
||||
hex.Multicodec(),
|
||||
b64.Multicodec(),
|
||||
bin.Multicodec(),
|
||||
}, mux.SelectFirst)
|
||||
m.Wrap = false
|
||||
return m
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package mc_cbor
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
cbor "github.com/whyrusleeping/cbor/go"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
)
|
||||
|
||||
var HeaderPath string
|
||||
var Header []byte
|
||||
|
||||
func init() {
|
||||
HeaderPath = "/cbor"
|
||||
Header = mc.Header([]byte(HeaderPath))
|
||||
}
|
||||
|
||||
type codec struct {
|
||||
mc bool
|
||||
}
|
||||
|
||||
func Codec() mc.Codec {
|
||||
return &codec{
|
||||
mc: false,
|
||||
}
|
||||
}
|
||||
|
||||
func Multicodec() mc.Multicodec {
|
||||
return &codec{
|
||||
mc: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codec) Encoder(w io.Writer) mc.Encoder {
|
||||
return &encoder{
|
||||
w: w,
|
||||
mc: c.mc,
|
||||
enc: cbor.NewEncoder(w),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codec) Decoder(r io.Reader) mc.Decoder {
|
||||
return &decoder{
|
||||
r: r,
|
||||
mc: c.mc,
|
||||
dec: cbor.NewDecoder(r),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codec) Header() []byte {
|
||||
return Header
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.Writer
|
||||
mc bool
|
||||
enc *cbor.Encoder
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
mc bool
|
||||
dec *cbor.Decoder
|
||||
}
|
||||
|
||||
func (c *encoder) Encode(v interface{}) error {
|
||||
// if multicodec, write the header first
|
||||
if c.mc {
|
||||
if _, err := c.w.Write(Header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.enc.Encode(v)
|
||||
}
|
||||
|
||||
func (c *decoder) Decode(v interface{}) error {
|
||||
// if multicodec, consume the header first
|
||||
if c.mc {
|
||||
if err := mc.ConsumeHeader(c.r, Header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.dec.Decode(v)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package multicodec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Codec is an algorithm for coding data from one representation
|
||||
// to another. For convenience, we define a codec in the usual
|
||||
// sense: a function and its inverse, to encode and decode.
|
||||
type Codec interface {
|
||||
// Decoder wraps given io.Reader and returns an object which
|
||||
// will decode bytes into objects.
|
||||
Decoder(r io.Reader) Decoder
|
||||
|
||||
// Encoder wraps given io.Writer and returns an Encoder
|
||||
Encoder(w io.Writer) Encoder
|
||||
}
|
||||
|
||||
// Encoder encodes objects into bytes and writes them to an
|
||||
// underlying io.Writer. Works like encoding.Marshal
|
||||
type Encoder interface {
|
||||
Encode(n interface{}) error
|
||||
}
|
||||
|
||||
// Decoder decodes objects from bytes from an underlying
|
||||
// io.Reader, into given object. Works like encoding.Unmarshal
|
||||
type Decoder interface {
|
||||
Decode(n interface{}) error
|
||||
}
|
||||
|
||||
// Marshal serializes an object to a []byte.
|
||||
func Marshal(c Codec, o interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := MarshalTo(c, &buf, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// MarshalTo serializes an object to a writer.
|
||||
func MarshalTo(c Codec, w io.Writer, o interface{}) error {
|
||||
return c.Encoder(w).Encode(o)
|
||||
}
|
||||
|
||||
// Unmarshal deserializes an object to a []byte.
|
||||
func Unmarshal(c Codec, buf []byte, o interface{}) error {
|
||||
return UnmarshalFrom(c, bytes.NewBuffer(buf), o)
|
||||
}
|
||||
|
||||
// UnmarshalFrom deserializes an objects from a reader.
|
||||
func UnmarshalFrom(c Codec, r io.Reader, o interface{}) error {
|
||||
return c.Decoder(r).Decode(o)
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package multicodec
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrType = errors.New("multicodec type error")
|
||||
ErrHeaderInvalid = errors.New("multicodec header invalid")
|
||||
ErrMismatch = errors.New("multicodec did not match")
|
||||
ErrVarints = errors.New("multicodec varints not yet implemented")
|
||||
)
|
||||
|
||||
// Header returns a multicodec header with the given path.
|
||||
func Header(path []byte) []byte {
|
||||
b, err := HeaderSafe(path)
|
||||
if err != nil {
|
||||
panic(err.Error)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// HeaderSafe works like Header but it returns error instead of calling panic
|
||||
func HeaderSafe(path []byte) ([]byte, error) {
|
||||
l := len(path) + 1 // + \n
|
||||
if l >= 127 {
|
||||
return nil, ErrVarints
|
||||
}
|
||||
|
||||
buf := make([]byte, l+1)
|
||||
buf[0] = byte(l)
|
||||
copy(buf[1:], path)
|
||||
buf[l] = '\n'
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// HeaderPath returns the multicodec path from header
|
||||
func HeaderPath(hdr []byte) []byte {
|
||||
hdr = hdr[1:]
|
||||
if hdr[len(hdr)-1] == '\n' {
|
||||
hdr = hdr[:len(hdr)-1]
|
||||
}
|
||||
return hdr
|
||||
}
|
||||
|
||||
// WriteHeader writes a multicodec header to a writer.
|
||||
// It uses the given path.
|
||||
func WriteHeader(w io.Writer, path []byte) error {
|
||||
hdr := Header(path)
|
||||
_, err := w.Write(hdr)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadHeader reads a multicodec header from a reader.
|
||||
// Returns the header found, or an error if the header
|
||||
// mismatched.
|
||||
func ReadHeader(r io.Reader) (path []byte, err error) {
|
||||
lbuf := make([]byte, 1)
|
||||
if _, err := r.Read(lbuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := int(lbuf[0])
|
||||
if l > 127 {
|
||||
return nil, ErrVarints
|
||||
}
|
||||
|
||||
buf := make([]byte, l+1)
|
||||
buf[0] = lbuf[0]
|
||||
if _, err := io.ReadFull(r, buf[1:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[l] != '\n' {
|
||||
return nil, ErrHeaderInvalid
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// ReadPath reads a multicodec header from a reader.
|
||||
// Returns the path found, or an error if the header
|
||||
// mismatched.
|
||||
func ReadPath(r io.Reader) (path []byte, err error) {
|
||||
hdr, err := ReadHeader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return HeaderPath(hdr), nil
|
||||
}
|
||||
|
||||
// ConsumePath reads a multicodec header from a reader,
|
||||
// verifying it matches given path. If it does not, it returns
|
||||
// ErrProtocolMismatch
|
||||
func ConsumePath(r io.Reader, path []byte) (err error) {
|
||||
actual, err := ReadPath(r)
|
||||
if !bytes.Equal(path, actual) {
|
||||
return ErrMismatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConsumeHeader reads a multicodec header from a reader,
|
||||
// verifying it matches given header. If it does not, it returns
|
||||
// ErrProtocolMismatch
|
||||
func ConsumeHeader(r io.Reader, header []byte) (err error) {
|
||||
actual := make([]byte, len(header))
|
||||
if _, err := io.ReadFull(r, actual); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(header, actual) {
|
||||
return ErrMismatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WrapHeaderReader returns a reader that first reads the
|
||||
// given header, and then the given reader, using io.MultiReader.
|
||||
// It is useful if the header has been read through, but still
|
||||
// needed to pass to a decoder.
|
||||
func WrapHeaderReader(hdr []byte, r io.Reader) io.Reader {
|
||||
return io.MultiReader(bytes.NewReader(hdr), r)
|
||||
}
|
||||
|
||||
func WrapTransformPathToHeader(r io.Reader) (io.Reader, error) {
|
||||
br := bufio.NewReader(r)
|
||||
|
||||
p, err := br.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = p[:len(p)-1] // drop newline
|
||||
hdr, err := HeaderSafe(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return WrapHeaderReader(hdr, br), nil
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package mc_json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
msgio "github.com/libp2p/go-msgio"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
)
|
||||
|
||||
var HeaderPath string
|
||||
var Header []byte
|
||||
var HeaderMsgioPath string
|
||||
var HeaderMsgio []byte
|
||||
|
||||
func init() {
|
||||
HeaderPath = "/json"
|
||||
HeaderMsgioPath = "/json/msgio"
|
||||
Header = mc.Header([]byte(HeaderPath))
|
||||
HeaderMsgio = mc.Header([]byte(HeaderMsgioPath))
|
||||
}
|
||||
|
||||
type codec struct {
|
||||
mc bool
|
||||
msgio bool
|
||||
}
|
||||
|
||||
func Codec(msgio bool) mc.Codec {
|
||||
return &codec{mc: false, msgio: msgio}
|
||||
}
|
||||
|
||||
func Multicodec(msgio bool) mc.Multicodec {
|
||||
return &codec{mc: true, msgio: msgio}
|
||||
}
|
||||
|
||||
func (c *codec) Encoder(w io.Writer) mc.Encoder {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
return &encoder{
|
||||
w: w,
|
||||
c: c,
|
||||
buf: buf,
|
||||
enc: json.NewEncoder(buf),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codec) Decoder(r io.Reader) mc.Decoder {
|
||||
return &decoder{
|
||||
r: r,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codec) Header() []byte {
|
||||
if c.msgio {
|
||||
return HeaderMsgio
|
||||
}
|
||||
return Header
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.Writer
|
||||
c *codec
|
||||
enc *json.Encoder
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
c *codec
|
||||
}
|
||||
|
||||
func (c *encoder) Encode(v interface{}) error {
|
||||
defer c.buf.Reset()
|
||||
w := c.w
|
||||
|
||||
if c.c.mc {
|
||||
// if multicodec, write the header first
|
||||
if _, err := c.w.Write(c.c.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.c.msgio {
|
||||
w = msgio.NewWriter(w)
|
||||
}
|
||||
|
||||
// recast to deal with map[interface{}]interface{} case
|
||||
vr, err := recast(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.enc.Encode(vr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, c.buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *decoder) Decode(v interface{}) error {
|
||||
r := c.r
|
||||
|
||||
if c.c.mc {
|
||||
// if multicodec, consume the header first
|
||||
if err := mc.ConsumeHeader(c.r, c.c.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.c.msgio {
|
||||
// need to make a new one per read.
|
||||
var err error
|
||||
r, err = msgio.LimitedReader(c.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return json.NewDecoder(r).Decode(v)
|
||||
}
|
||||
|
||||
func recast(v interface{}) (cv interface{}, err error) {
|
||||
switch v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
vmi := v.(map[interface{}]interface{})
|
||||
vms := make(map[string]interface{}, len(vmi))
|
||||
for k, v2 := range vmi {
|
||||
ks, ok := k.(string)
|
||||
if !ok {
|
||||
return v, mc.ErrType
|
||||
}
|
||||
|
||||
rv2, err := recast(v2)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
vms[ks] = rv2
|
||||
}
|
||||
return vms, nil
|
||||
default:
|
||||
return v, nil // hope for the best.
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package multicodec
|
||||
|
||||
import "io"
|
||||
|
||||
// Multicodec is the interface for a multicodec
|
||||
type Multicodec interface {
|
||||
Codec
|
||||
|
||||
Header() []byte
|
||||
}
|
||||
|
||||
type c2mc struct {
|
||||
c Codec
|
||||
header []byte
|
||||
}
|
||||
|
||||
var _ Multicodec = (*c2mc)(nil)
|
||||
|
||||
func (c c2mc) Header() []byte {
|
||||
return c.header
|
||||
}
|
||||
|
||||
type c2mcD struct {
|
||||
base *c2mc
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (d c2mcD) Decode(v interface{}) error {
|
||||
err := ConsumeHeader(d.r, d.base.header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.base.c.Decoder(d.r).Decode(v)
|
||||
}
|
||||
|
||||
var _ Decoder = (*c2mcD)(nil)
|
||||
|
||||
func (c c2mc) Decoder(r io.Reader) Decoder {
|
||||
return c2mcD{
|
||||
base: &c,
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
type c2mcE struct {
|
||||
base *c2mc
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (e c2mcE) Encode(v interface{}) error {
|
||||
_, err := e.w.Write(e.base.Header())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.base.c.Encoder(e.w).Encode(v)
|
||||
}
|
||||
|
||||
func (c c2mc) Encoder(w io.Writer) Encoder {
|
||||
return c2mcE{
|
||||
base: &c,
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMulticodecFromCodec(c Codec, header []byte) Multicodec {
|
||||
return c2mc{
|
||||
c: c,
|
||||
header: header,
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package muxcodec
|
||||
|
||||
import (
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
cbor "github.com/multiformats/go-multicodec/cbor"
|
||||
json "github.com/multiformats/go-multicodec/json"
|
||||
)
|
||||
|
||||
func StandardMux() *Multicodec {
|
||||
return MuxMulticodec([]mc.Multicodec{
|
||||
cbor.Multicodec(),
|
||||
json.Multicodec(false),
|
||||
json.Multicodec(true),
|
||||
}, SelectFirst)
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package muxcodec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
mc "github.com/multiformats/go-multicodec"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoCodec = fmt.Errorf("no suitable codec")
|
||||
)
|
||||
|
||||
var Header []byte
|
||||
|
||||
func init() {
|
||||
Header = mc.Header([]byte("/multicodec"))
|
||||
}
|
||||
|
||||
// SelectCodec is a function that selects which codecs are able to
|
||||
// marshal a given datastructure, and orders them (to be tried first).
|
||||
type SelectCodec func(v interface{}, codecs []mc.Multicodec) mc.Multicodec
|
||||
|
||||
// SelectFirst is the default SelectCodec function. selects the first
|
||||
// codec given.
|
||||
func SelectFirst(v interface{}, codecs []mc.Multicodec) mc.Multicodec {
|
||||
return codecs[0]
|
||||
}
|
||||
|
||||
// MuxMulticodec returns a multicodec that muxes between given codecs.
|
||||
// It uses the given SelectCodec function when marshaling, to select
|
||||
// the best codec to use.
|
||||
func MuxMulticodec(codecs []mc.Multicodec, sel SelectCodec) *Multicodec {
|
||||
if sel == nil {
|
||||
sel = SelectFirst
|
||||
}
|
||||
return &Multicodec{codecs, sel, true, nil}
|
||||
}
|
||||
|
||||
type Multicodec struct {
|
||||
Codecs []mc.Multicodec // subcodecs to use
|
||||
Select SelectCodec // pick a subcodec for encoding
|
||||
Wrap bool // whether to wrap with own header
|
||||
Last mc.Multicodec // the last subcodec used
|
||||
}
|
||||
|
||||
func (c *Multicodec) Encoder(w io.Writer) mc.Encoder {
|
||||
return &encoder{w, c}
|
||||
}
|
||||
|
||||
func (c *Multicodec) Decoder(r io.Reader) mc.Decoder {
|
||||
return &decoder{r, c}
|
||||
}
|
||||
|
||||
func (c *Multicodec) Header() []byte {
|
||||
return Header
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w io.Writer
|
||||
c *Multicodec
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
r io.Reader
|
||||
c *Multicodec
|
||||
}
|
||||
|
||||
func (c *encoder) Encode(v interface{}) error {
|
||||
subc := c.c.Select(v, c.c.Codecs)
|
||||
if subc == nil {
|
||||
return ErrNoCodec
|
||||
}
|
||||
|
||||
if c.c.Wrap { // write multicodec header.
|
||||
if _, err := c.w.Write(c.c.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.c.Last = subc
|
||||
return subc.Encoder(c.w).Encode(v)
|
||||
}
|
||||
|
||||
func (c *decoder) Decode(v interface{}) error {
|
||||
if c.c.Wrap { // read multicodec header.
|
||||
if err := mc.ConsumeHeader(c.r, c.c.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// get next header, to select codec
|
||||
hdr, err := mc.ReadHeader(c.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// "unwind" the read as subc consumes header
|
||||
r := mc.WrapHeaderReader(hdr, c.r)
|
||||
|
||||
subc := CodecWithHeader(hdr, c.c.Codecs)
|
||||
if subc == nil {
|
||||
return fmt.Errorf("no codec for %s", hdr)
|
||||
}
|
||||
|
||||
c.c.Last = subc
|
||||
return subc.Decoder(r).Decode(v)
|
||||
}
|
||||
|
||||
func CodecWithHeader(hdr []byte, codecs []mc.Multicodec) mc.Multicodec {
|
||||
// we'll look through the list. should be small.
|
||||
// if huge, consider a map.
|
||||
for _, c := range codecs {
|
||||
if bytes.Equal(hdr, c.Header()) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"author": "multiformats",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multicodec/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multicodec"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmWBug6eBS7AxRdCDVuSY5CnSit7cS2XnPFYJWqWDumhCG",
|
||||
"name": "go-msgio",
|
||||
"version": "0.0.3"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmcRKRQjNc2JZPHApR32fbkZVd6WXG2Ch9Kcy7sPxuAJgd",
|
||||
"name": "cbor",
|
||||
"version": "0.2.3"
|
||||
},
|
||||
{
|
||||
"author": "ugorji",
|
||||
"hash": "QmVTAmbCaPqdfbmpWDCJMQNFxbyJoG2USFsumXmTWY5LFp",
|
||||
"name": "go-codec",
|
||||
"version": "2017.10.18"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8",
|
||||
"name": "gogo-protobuf",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.10.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "go-multicodec",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "0.1.6"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.vscode/
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -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.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
gx:
|
||||
go get github.com/whyrusleeping/gx
|
||||
go get github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
|
||||
publish:
|
||||
gx-go rewrite --undo
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
# go-multihash
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/multiformats/go-multihash)
|
||||
[](https://travis-ci.org/multiformats/go-multihash)
|
||||
[](https://codecov.io/github/multiformats/go-multihash?branch=master)
|
||||
|
||||
> [multihash](https://github.com/multiformats/multihash) implementation in Go
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
`go-multihash` is a standard Go module which can be installed with:
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multihash
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
This example takes a standard hex-encoded data and uses `EncodeName` to calculate the SHA1 multihash value for the buffer.
|
||||
|
||||
The resulting hex-encoded data corresponds to: `<hash function code><digest size><hash function output>`, which could be re-parsed
|
||||
with `Multihash.FromHexString()`.
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// ignores errors for simplicity.
|
||||
// don't do that at home.
|
||||
// Decode a SHA1 hash to a binary buffer
|
||||
buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
|
||||
|
||||
// Create a new multihash with it.
|
||||
mHashBuf, _ := multihash.EncodeName(buf, "sha1")
|
||||
// Print the multihash as hex string
|
||||
fmt.Printf("hex: %s\n", hex.EncodeToString(mHashBuf))
|
||||
|
||||
// Parse the binary multihash to a DecodedMultihash
|
||||
mHash, _ := multihash.Decode(mHashBuf)
|
||||
// Convert the sha1 value to hex string
|
||||
sha1hex := hex.EncodeToString(mHash.Digest)
|
||||
// Print all the information in the multihash
|
||||
fmt.Printf("obj: %v 0x%x %d %s\n", mHash.Name, mHash.Code, mHash.Length, sha1hex)
|
||||
}
|
||||
```
|
||||
|
||||
To run, copy to [example/foo.go](example/foo.go) and:
|
||||
|
||||
```
|
||||
> cd example/
|
||||
> go build
|
||||
> ./example
|
||||
hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
|
||||
obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multihash/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2014 Juan Batiz-Benet
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
coverage:
|
||||
range: "50...100"
|
||||
comment: off
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
module github.com/multiformats/go-multihash
|
||||
|
||||
require (
|
||||
github.com/gxed/hashland/keccakpg v0.0.1
|
||||
github.com/gxed/hashland/murmur3 v0.0.1
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16
|
||||
github.com/mr-tron/base58 v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d // indirect
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package multihash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 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) ReadByte() (byte, error) {
|
||||
if br, ok := r.r.(io.ByteReader); ok {
|
||||
return br.ReadByte()
|
||||
}
|
||||
var b [1]byte
|
||||
n, err := r.r.Read(b[:])
|
||||
if n == 1 {
|
||||
return b[0], nil
|
||||
}
|
||||
if err == nil {
|
||||
if n != 0 {
|
||||
panic("reader returned an invalid length")
|
||||
}
|
||||
err = io.ErrNoProgress
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (r *mhReader) ReadMultihash() (Multihash, error) {
|
||||
code, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length > math.MaxInt32 {
|
||||
return nil, errors.New("digest too long, supporting only <= 2^31-1")
|
||||
}
|
||||
|
||||
pre := make([]byte, 2*binary.MaxVarintLen64)
|
||||
spot := pre
|
||||
n := binary.PutUvarint(spot, code)
|
||||
spot = pre[n:]
|
||||
n += binary.PutUvarint(spot, length)
|
||||
|
||||
buf := make([]byte, int(length)+n)
|
||||
copy(buf, pre[:n])
|
||||
|
||||
if _, err := io.ReadFull(r.r, buf[n:]); 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
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
// Package multihash is the Go implementation of
|
||||
// https://github.com/multiformats/multihash, or self-describing
|
||||
// hashes.
|
||||
package multihash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
b58 "github.com/mr-tron/base58/base58"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrUnknownCode = errors.New("unknown multihash code")
|
||||
ErrTooShort = errors.New("multihash too short. must be >= 2 bytes")
|
||||
ErrTooLong = errors.New("multihash too long. must be < 129 bytes")
|
||||
ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes")
|
||||
ErrInvalidMultihash = errors.New("input isn't valid multihash")
|
||||
|
||||
ErrVarintBufferShort = errors.New("uvarint: buffer too small")
|
||||
ErrVarintTooLong = errors.New("uvarint: varint too big (max 64bit)")
|
||||
)
|
||||
|
||||
// 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 (
|
||||
ID = 0x00
|
||||
SHA1 = 0x11
|
||||
SHA2_256 = 0x12
|
||||
SHA2_512 = 0x13
|
||||
SHA3_224 = 0x17
|
||||
SHA3_256 = 0x16
|
||||
SHA3_384 = 0x15
|
||||
SHA3_512 = 0x14
|
||||
SHA3 = SHA3_512
|
||||
KECCAK_224 = 0x1A
|
||||
KECCAK_256 = 0x1B
|
||||
KECCAK_384 = 0x1C
|
||||
KECCAK_512 = 0x1D
|
||||
|
||||
SHAKE_128 = 0x18
|
||||
SHAKE_256 = 0x19
|
||||
|
||||
BLAKE2B_MIN = 0xb201
|
||||
BLAKE2B_MAX = 0xb240
|
||||
BLAKE2S_MIN = 0xb241
|
||||
BLAKE2S_MAX = 0xb260
|
||||
|
||||
MD5 = 0xd5
|
||||
|
||||
DBL_SHA2_256 = 0x56
|
||||
|
||||
MURMUR3 = 0x22
|
||||
|
||||
X11 = 0x1100
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Add blake2b (64 codes)
|
||||
for c := uint64(BLAKE2B_MIN); c <= BLAKE2B_MAX; c++ {
|
||||
n := c - BLAKE2B_MIN + 1
|
||||
name := fmt.Sprintf("blake2b-%d", n*8)
|
||||
Names[name] = c
|
||||
Codes[c] = name
|
||||
DefaultLengths[c] = int(n)
|
||||
}
|
||||
|
||||
// Add blake2s (32 codes)
|
||||
for c := uint64(BLAKE2S_MIN); c <= BLAKE2S_MAX; c++ {
|
||||
n := c - BLAKE2S_MIN + 1
|
||||
name := fmt.Sprintf("blake2s-%d", n*8)
|
||||
Names[name] = c
|
||||
Codes[c] = name
|
||||
DefaultLengths[c] = int(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Names maps the name of a hash to the code
|
||||
var Names = map[string]uint64{
|
||||
"id": ID,
|
||||
"sha1": SHA1,
|
||||
"sha2-256": SHA2_256,
|
||||
"sha2-512": SHA2_512,
|
||||
"sha3": SHA3_512,
|
||||
"sha3-224": SHA3_224,
|
||||
"sha3-256": SHA3_256,
|
||||
"sha3-384": SHA3_384,
|
||||
"sha3-512": SHA3_512,
|
||||
"dbl-sha2-256": DBL_SHA2_256,
|
||||
"murmur3": MURMUR3,
|
||||
"keccak-224": KECCAK_224,
|
||||
"keccak-256": KECCAK_256,
|
||||
"keccak-384": KECCAK_384,
|
||||
"keccak-512": KECCAK_512,
|
||||
"shake-128": SHAKE_128,
|
||||
"shake-256": SHAKE_256,
|
||||
"x11": X11,
|
||||
"md5": MD5,
|
||||
}
|
||||
|
||||
// Codes maps a hash code to it's name
|
||||
var Codes = map[uint64]string{
|
||||
ID: "id",
|
||||
SHA1: "sha1",
|
||||
SHA2_256: "sha2-256",
|
||||
SHA2_512: "sha2-512",
|
||||
SHA3_224: "sha3-224",
|
||||
SHA3_256: "sha3-256",
|
||||
SHA3_384: "sha3-384",
|
||||
SHA3_512: "sha3-512",
|
||||
DBL_SHA2_256: "dbl-sha2-256",
|
||||
MURMUR3: "murmur3",
|
||||
KECCAK_224: "keccak-224",
|
||||
KECCAK_256: "keccak-256",
|
||||
KECCAK_384: "keccak-384",
|
||||
KECCAK_512: "keccak-512",
|
||||
SHAKE_128: "shake-128",
|
||||
SHAKE_256: "shake-256",
|
||||
X11: "x11",
|
||||
MD5: "md5",
|
||||
}
|
||||
|
||||
// DefaultLengths maps a hash code to it's default length
|
||||
var DefaultLengths = map[uint64]int{
|
||||
ID: -1,
|
||||
SHA1: 20,
|
||||
SHA2_256: 32,
|
||||
SHA2_512: 64,
|
||||
SHA3_224: 28,
|
||||
SHA3_256: 32,
|
||||
SHA3_384: 48,
|
||||
SHA3_512: 64,
|
||||
DBL_SHA2_256: 32,
|
||||
KECCAK_224: 28,
|
||||
KECCAK_256: 32,
|
||||
MURMUR3: 4,
|
||||
KECCAK_384: 48,
|
||||
KECCAK_512: 64,
|
||||
SHAKE_128: 32,
|
||||
SHAKE_256: 64,
|
||||
X11: 64,
|
||||
MD5: 16,
|
||||
}
|
||||
|
||||
func uvarint(buf []byte) (uint64, []byte, error) {
|
||||
n, c := binary.Uvarint(buf)
|
||||
|
||||
if c == 0 {
|
||||
return n, buf, ErrVarintBufferShort
|
||||
} else if c < 0 {
|
||||
return n, buf[-c:], ErrVarintTooLong
|
||||
} else {
|
||||
return n, buf[c:], nil
|
||||
}
|
||||
}
|
||||
|
||||
// DecodedMultihash represents a parsed multihash and allows
|
||||
// easy access to the different parts of a multihash.
|
||||
type DecodedMultihash struct {
|
||||
Code uint64
|
||||
Name string
|
||||
Length int // Length is just int as it is type of len() opearator
|
||||
Digest []byte // Digest holds the raw multihash bytes
|
||||
}
|
||||
|
||||
// Multihash is byte slice with the following form:
|
||||
// <hash function code><digest size><hash function output>.
|
||||
// See the spec for more information.
|
||||
type Multihash []byte
|
||||
|
||||
// HexString returns the hex-encoded representation of a multihash.
|
||||
func (m *Multihash) HexString() string {
|
||||
return hex.EncodeToString([]byte(*m))
|
||||
}
|
||||
|
||||
// String is an alias to HexString().
|
||||
func (m *Multihash) String() string {
|
||||
return m.HexString()
|
||||
}
|
||||
|
||||
// FromHexString parses a hex-encoded multihash.
|
||||
func FromHexString(s string) (Multihash, error) {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return Multihash{}, err
|
||||
}
|
||||
|
||||
return Cast(b)
|
||||
}
|
||||
|
||||
// B58String returns the B58-encoded representation of a multihash.
|
||||
func (m Multihash) B58String() string {
|
||||
return b58.Encode([]byte(m))
|
||||
}
|
||||
|
||||
// FromB58String parses a B58-encoded multihash.
|
||||
func FromB58String(s string) (m Multihash, err error) {
|
||||
b, err := b58.Decode(s)
|
||||
if err != nil {
|
||||
return Multihash{}, ErrInvalidMultihash
|
||||
}
|
||||
|
||||
return Cast(b)
|
||||
}
|
||||
|
||||
// Cast casts a buffer onto a multihash, and returns an error
|
||||
// if it does not work.
|
||||
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 parses multihash bytes into a DecodedMultihash.
|
||||
func Decode(buf []byte) (*DecodedMultihash, error) {
|
||||
|
||||
if len(buf) < 2 {
|
||||
return nil, ErrTooShort
|
||||
}
|
||||
|
||||
var err error
|
||||
var code, length uint64
|
||||
|
||||
code, buf, err = uvarint(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length, buf, err = uvarint(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length > math.MaxInt32 {
|
||||
return nil, errors.New("digest too long, supporting only <= 2^31-1")
|
||||
}
|
||||
|
||||
dm := &DecodedMultihash{
|
||||
Code: code,
|
||||
Name: Codes[code],
|
||||
Length: int(length),
|
||||
Digest: buf,
|
||||
}
|
||||
|
||||
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 uint64) ([]byte, error) {
|
||||
|
||||
if !ValidCode(code) {
|
||||
return nil, ErrUnknownCode
|
||||
}
|
||||
|
||||
start := make([]byte, 2*binary.MaxVarintLen64, 2*binary.MaxVarintLen64+len(buf))
|
||||
spot := start
|
||||
n := binary.PutUvarint(spot, code)
|
||||
spot = start[n:]
|
||||
n += binary.PutUvarint(spot, uint64(len(buf)))
|
||||
|
||||
return append(start[:n], buf...), nil
|
||||
}
|
||||
|
||||
// EncodeName is like Encode() but providing a string name
|
||||
// instead of a numeric code. See Names for allowed values.
|
||||
func EncodeName(buf []byte, name string) ([]byte, error) {
|
||||
return Encode(buf, Names[name])
|
||||
}
|
||||
|
||||
// ValidCode checks whether a multihash code is valid.
|
||||
func ValidCode(code uint64) bool {
|
||||
_, ok := Codes[code]
|
||||
return ok
|
||||
}
|
||||
+21
@@ -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.
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"author": "multiformats",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multihash/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multihash"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmW7VUmSvhvSGbYbdsh7uRjhGmsYkc9fL8aJ5CorxxrU5N",
|
||||
"name": "go-crypto",
|
||||
"version": "0.2.1"
|
||||
},
|
||||
{
|
||||
"author": "mr-tron",
|
||||
"hash": "QmWFAMPqsEyUX7gDUsRVmMWz59FxSpJ1b2v6bJ1yYzo7jY",
|
||||
"name": "go-base58-fast",
|
||||
"version": "0.1.1"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmZtJMfZZvoD3EKpQaf8xsFi83HMtX5acQekY8exMbcWEi",
|
||||
"name": "keccakpg",
|
||||
"version": "0.0.1"
|
||||
},
|
||||
{
|
||||
"author": "minio",
|
||||
"hash": "QmcTzQXRcU2vf8yX5EEboz1BSvWC7wWmeYAKVQmhp8WZYU",
|
||||
"name": "sha256-simd",
|
||||
"version": "0.1.2"
|
||||
},
|
||||
{
|
||||
"author": "minio",
|
||||
"hash": "QmZp3eKdYQHHAneECmeK6HhiMwTPufmjC8DuuaGKv3unvx",
|
||||
"name": "blake2b-simd",
|
||||
"version": "0.1.1"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmWAXZgFyppTRshtnVHJ8LnA1yoHjUr41ZnsWPoA8wnSgF",
|
||||
"name": "hashland-murmur3",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.9.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "go-multihash",
|
||||
"releaseCmd": "git commit -a -m \"gx release $VERSION\"",
|
||||
"version": "1.0.10"
|
||||
}
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
package multihash
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
keccak "github.com/gxed/hashland/keccakpg"
|
||||
murmur3 "github.com/gxed/hashland/murmur3"
|
||||
blake2b "github.com/minio/blake2b-simd"
|
||||
sha256 "github.com/minio/sha256-simd"
|
||||
blake2s "golang.org/x/crypto/blake2s"
|
||||
sha3 "golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// ErrSumNotSupported is returned when the Sum function code is not implemented
|
||||
var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.")
|
||||
|
||||
// HashFunc is a hash function that hashes data into digest.
|
||||
//
|
||||
// The length is the size the digest will be truncated to. While the hash
|
||||
// function isn't responsible for truncating the digest, it may want to error if
|
||||
// the length is invalid for the hash function (e.g., truncation would make the
|
||||
// hash useless).
|
||||
type HashFunc func(data []byte, length int) (digest []byte, err error)
|
||||
|
||||
// funcTable maps multicodec values to hash functions.
|
||||
var funcTable = make(map[uint64]HashFunc)
|
||||
|
||||
// Sum obtains the cryptographic sum of a given buffer. The length parameter
|
||||
// indicates the length of the resulting digest and passing a negative value
|
||||
// use default length values for the selected hash function.
|
||||
func Sum(data []byte, code uint64, length int) (Multihash, error) {
|
||||
if !ValidCode(code) {
|
||||
return nil, fmt.Errorf("invalid multihash code %d", code)
|
||||
}
|
||||
|
||||
if length < 0 {
|
||||
var ok bool
|
||||
length, ok = DefaultLengths[code]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no default length for code %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
hashFunc, ok := funcTable[code]
|
||||
if !ok {
|
||||
return nil, ErrSumNotSupported
|
||||
}
|
||||
|
||||
d, err := hashFunc(data, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length >= 0 {
|
||||
d = d[:length]
|
||||
}
|
||||
return Encode(d, code)
|
||||
}
|
||||
|
||||
func sumBlake2s(data []byte, size int) ([]byte, error) {
|
||||
if size != 32 {
|
||||
return nil, fmt.Errorf("unsupported length for blake2s: %d", size)
|
||||
}
|
||||
d := blake2s.Sum256(data)
|
||||
return d[:], nil
|
||||
}
|
||||
func sumBlake2b(data []byte, size int) ([]byte, error) {
|
||||
hasher, err := blake2b.New(&blake2b.Config{Size: uint8(size)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := hasher.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hasher.Sum(nil)[:], nil
|
||||
}
|
||||
|
||||
func sumID(data []byte, length int) ([]byte, error) {
|
||||
if length >= 0 && length != len(data) {
|
||||
return nil, fmt.Errorf("the length of the identity hash (%d) must be equal to the length of the data (%d)",
|
||||
length, len(data))
|
||||
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func sumSHA1(data []byte, length int) ([]byte, error) {
|
||||
a := sha1.Sum(data)
|
||||
return a[0:20], nil
|
||||
}
|
||||
|
||||
func sumSHA256(data []byte, length int) ([]byte, error) {
|
||||
a := sha256.Sum256(data)
|
||||
return a[0:32], nil
|
||||
}
|
||||
|
||||
func sumMD5(data []byte, length int) ([]byte, error) {
|
||||
a := md5.Sum(data)
|
||||
return a[0:md5.Size], nil
|
||||
}
|
||||
|
||||
func sumDoubleSHA256(data []byte, length int) ([]byte, error) {
|
||||
val, _ := sumSHA256(data, len(data))
|
||||
return sumSHA256(val, len(val))
|
||||
}
|
||||
|
||||
func sumSHA512(data []byte, length int) ([]byte, error) {
|
||||
a := sha512.Sum512(data)
|
||||
return a[0:64], nil
|
||||
}
|
||||
|
||||
func sumKeccak224(data []byte, length int) ([]byte, error) {
|
||||
h := keccak.New224()
|
||||
h.Write(data)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func sumKeccak256(data []byte, length int) ([]byte, error) {
|
||||
h := keccak.New256()
|
||||
h.Write(data)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func sumKeccak384(data []byte, length int) ([]byte, error) {
|
||||
h := keccak.New384()
|
||||
h.Write(data)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func sumKeccak512(data []byte, length int) ([]byte, error) {
|
||||
h := keccak.New512()
|
||||
h.Write(data)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func sumSHA3_512(data []byte, length int) ([]byte, error) {
|
||||
a := sha3.Sum512(data)
|
||||
return a[:], nil
|
||||
}
|
||||
|
||||
func sumMURMUR3(data []byte, length int) ([]byte, error) {
|
||||
number := murmur3.Sum32(data)
|
||||
bytes := make([]byte, 4)
|
||||
for i := range bytes {
|
||||
bytes[i] = byte(number & 0xff)
|
||||
number >>= 8
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func sumSHAKE128(data []byte, length int) ([]byte, error) {
|
||||
bytes := make([]byte, 32)
|
||||
sha3.ShakeSum128(bytes, data)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func sumSHAKE256(data []byte, length int) ([]byte, error) {
|
||||
bytes := make([]byte, 64)
|
||||
sha3.ShakeSum256(bytes, data)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func sumSHA3_384(data []byte, length int) ([]byte, error) {
|
||||
a := sha3.Sum384(data)
|
||||
return a[:], nil
|
||||
}
|
||||
|
||||
func sumSHA3_256(data []byte, length int) ([]byte, error) {
|
||||
a := sha3.Sum256(data)
|
||||
return a[:], nil
|
||||
}
|
||||
|
||||
func sumSHA3_224(data []byte, length int) ([]byte, error) {
|
||||
a := sha3.Sum224(data)
|
||||
return a[:], nil
|
||||
}
|
||||
|
||||
func registerStdlibHashFuncs() {
|
||||
RegisterHashFunc(ID, sumID)
|
||||
RegisterHashFunc(SHA1, sumSHA1)
|
||||
RegisterHashFunc(SHA2_512, sumSHA512)
|
||||
RegisterHashFunc(MD5, sumMD5)
|
||||
}
|
||||
|
||||
func registerNonStdlibHashFuncs() {
|
||||
RegisterHashFunc(SHA2_256, sumSHA256)
|
||||
RegisterHashFunc(DBL_SHA2_256, sumDoubleSHA256)
|
||||
|
||||
RegisterHashFunc(KECCAK_224, sumKeccak224)
|
||||
RegisterHashFunc(KECCAK_256, sumKeccak256)
|
||||
RegisterHashFunc(KECCAK_384, sumKeccak384)
|
||||
RegisterHashFunc(KECCAK_512, sumKeccak512)
|
||||
|
||||
RegisterHashFunc(SHA3_224, sumSHA3_224)
|
||||
RegisterHashFunc(SHA3_256, sumSHA3_256)
|
||||
RegisterHashFunc(SHA3_384, sumSHA3_384)
|
||||
RegisterHashFunc(SHA3_512, sumSHA3_512)
|
||||
|
||||
RegisterHashFunc(MURMUR3, sumMURMUR3)
|
||||
|
||||
RegisterHashFunc(SHAKE_128, sumSHAKE128)
|
||||
RegisterHashFunc(SHAKE_256, sumSHAKE256)
|
||||
|
||||
// Blake family of hash functions
|
||||
// BLAKE2S
|
||||
for c := uint64(BLAKE2S_MIN); c <= BLAKE2S_MAX; c++ {
|
||||
size := int(c - BLAKE2S_MIN + 1)
|
||||
RegisterHashFunc(c, func(buf []byte, _ int) ([]byte, error) {
|
||||
return sumBlake2s(buf, size)
|
||||
})
|
||||
}
|
||||
// BLAKE2B
|
||||
for c := uint64(BLAKE2B_MIN); c <= BLAKE2B_MAX; c++ {
|
||||
size := int(c - BLAKE2B_MIN + 1)
|
||||
RegisterHashFunc(c, func(buf []byte, _ int) ([]byte, error) {
|
||||
return sumBlake2b(buf, size)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerStdlibHashFuncs()
|
||||
registerNonStdlibHashFuncs()
|
||||
}
|
||||
|
||||
// RegisterHashFunc adds an entry to the package-level code -> hash func map.
|
||||
// The hash function must return at least the requested number of bytes. If it
|
||||
// returns more, the hash will be truncated.
|
||||
func RegisterHashFunc(code uint64, hashFunc HashFunc) error {
|
||||
if !ValidCode(code) {
|
||||
return fmt.Errorf("code %v not valid", code)
|
||||
}
|
||||
|
||||
_, ok := funcTable[code]
|
||||
if ok {
|
||||
return fmt.Errorf("hash func for code %v already registered", code)
|
||||
}
|
||||
|
||||
funcTable[code] = hashFunc
|
||||
return nil
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Jeromy Johnson
|
||||
|
||||
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.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
export IPFS_API ?= v04x.ipfs.io
|
||||
|
||||
gx:
|
||||
go get github.com/whyrusleeping/gx
|
||||
go get github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
|
||||
publish:
|
||||
gx-go rewrite --undo
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
# go-multistream
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/multiformats/go-multistream)
|
||||
[](https://travis-ci.org/multiformats/go-multistream)
|
||||
[](https://codecov.io/github/multiformats/go-multistream?branch=master)
|
||||
|
||||
> an implementation of the multistream protocol in go
|
||||
|
||||
This package implements a simple stream router for the multistream-select protocol.
|
||||
The protocol is defined [here](https://github.com/multiformats/multistream-select).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
`go-multistream` is a standard Go module which can be installed with:
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multistream
|
||||
```
|
||||
|
||||
Note that `go-multistream` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Using Gx and Gx-go
|
||||
|
||||
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project do:
|
||||
|
||||
```sh
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
cd <your-project-repository>
|
||||
gx init
|
||||
gx import github.com/multiformats/go-multistream
|
||||
gx install --global
|
||||
gx-go --rewrite
|
||||
```
|
||||
|
||||
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
This example shows how to use a multistream muxer. A muxer uses user-added handlers to handle different "protocols". The first step when interacting with a connection handler by the muxer is to select the protocol (the example uses `SelectProtoOrFail`). This will then let the muxer use the right handler.
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
ms "github.com/multiformats/go-multistream"
|
||||
)
|
||||
|
||||
// This example creates a multistream muxer, adds handlers for the protocols
|
||||
// "/cats" and "/dogs" and exposes it on a localhost:8765. It then opens connections
|
||||
// to that port, selects the protocols and tests that the handlers are working.
|
||||
func main() {
|
||||
mux := ms.NewMultistreamMuxer()
|
||||
mux.AddHandler("/cats", func(proto string, rwc io.ReadWriteCloser) error {
|
||||
fmt.Fprintln(rwc, proto, ": HELLO I LIKE CATS")
|
||||
return rwc.Close()
|
||||
})
|
||||
mux.AddHandler("/dogs", func(proto string, rwc io.ReadWriteCloser) error {
|
||||
fmt.Fprintln(rwc, proto, ": HELLO I LIKE DOGS")
|
||||
return rwc.Close()
|
||||
})
|
||||
|
||||
list, err := net.Listen("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
con, err := list.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go mux.Handle(con)
|
||||
}
|
||||
}()
|
||||
|
||||
// The Muxer is ready, let's test it
|
||||
conn, err := net.Dial("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create a new multistream to talk to the muxer
|
||||
// which will negotiate that we want to talk with /cats
|
||||
mstream := ms.NewMSSelect(conn, "/cats")
|
||||
cats, err := ioutil.ReadAll(mstream)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s", cats)
|
||||
mstream.Close()
|
||||
|
||||
// A different way of talking to the muxer
|
||||
// is to manually selecting the protocol ourselves
|
||||
conn, err = net.Dial("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = ms.SelectProtoOrFail("/dogs", conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dogs, err := ioutil.ReadAll(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s", dogs)
|
||||
conn.Close()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Maintainers
|
||||
|
||||
Captain: [@whyrusleeping](https://github.com/whyrusleeping).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multistream/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2016 Jeromy Johnson
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrNotSupported is the error returned when the muxer does not support
|
||||
// the protocol specified for the handshake.
|
||||
var ErrNotSupported = errors.New("protocol not supported")
|
||||
|
||||
// SelectProtoOrFail performs the initial multistream handshake
|
||||
// to inform the muxer of the protocol that will be used to communicate
|
||||
// on this ReadWriteCloser. It returns an error if, for example,
|
||||
// the muxer does not know how to handle this protocol.
|
||||
func SelectProtoOrFail(proto string, rwc io.ReadWriteCloser) error {
|
||||
err := handshake(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return trySelect(proto, rwc)
|
||||
}
|
||||
|
||||
// SelectOneOf will perform handshakes with the protocols on the given slice
|
||||
// until it finds one which is supported by the muxer.
|
||||
func SelectOneOf(protos []string, rwc io.ReadWriteCloser) (string, error) {
|
||||
err := handshake(rwc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, p := range protos {
|
||||
err := trySelect(p, rwc)
|
||||
switch err {
|
||||
case nil:
|
||||
return p, nil
|
||||
case ErrNotSupported:
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", ErrNotSupported
|
||||
}
|
||||
|
||||
func handshake(rwc io.ReadWriteCloser) error {
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
errCh <- delimWriteBuffered(rwc, []byte(ProtocolID))
|
||||
}()
|
||||
|
||||
tok, readErr := ReadNextToken(rwc)
|
||||
writeErr := <-errCh
|
||||
|
||||
if writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
|
||||
if tok != ProtocolID {
|
||||
return errors.New("received mismatch in protocol id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func trySelect(proto string, rwc io.ReadWriteCloser) error {
|
||||
err := delimWriteBuffered(rwc, []byte(proto))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case proto:
|
||||
return nil
|
||||
case "na":
|
||||
return ErrNotSupported
|
||||
default:
|
||||
return errors.New("unrecognized response: " + tok)
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/multiformats/go-multistream
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Multistream represents in essense a ReadWriteCloser, or a single
|
||||
// communication wire which supports multiple streams on it. Each
|
||||
// stream is identified by a protocol tag.
|
||||
type Multistream interface {
|
||||
io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// NewMSSelect returns a new Multistream which is able to perform
|
||||
// protocol selection with a MultistreamMuxer.
|
||||
func NewMSSelect(c io.ReadWriteCloser, proto string) Multistream {
|
||||
return &lazyClientConn{
|
||||
protos: []string{ProtocolID, proto},
|
||||
con: c,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMultistream returns a multistream for the given protocol. This will not
|
||||
// perform any protocol selection. If you are using a MultistreamMuxer, use
|
||||
// NewMSSelect.
|
||||
func NewMultistream(c io.ReadWriteCloser, proto string) Multistream {
|
||||
return &lazyClientConn{
|
||||
protos: []string{proto},
|
||||
con: c,
|
||||
}
|
||||
}
|
||||
|
||||
// lazyClientConn is a ReadWriteCloser adapter that lazily negotiates a protocol
|
||||
// using multistream-select on first use.
|
||||
//
|
||||
// It *does not* block writes waiting for the other end to respond. Instead, it
|
||||
// simply assumes the negotiation went successfully and starts writing data.
|
||||
// See: https://github.com/multiformats/go-multistream/issues/20
|
||||
type lazyClientConn struct {
|
||||
// Used to ensure we only trigger the write half of the handshake once.
|
||||
rhandshakeOnce sync.Once
|
||||
rerr error
|
||||
|
||||
// Used to ensure we only trigger the read half of the handshake once.
|
||||
whandshakeOnce sync.Once
|
||||
werr error
|
||||
|
||||
// The sequence of protocols to negotiate.
|
||||
protos []string
|
||||
|
||||
// The inner connection.
|
||||
con io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// Read reads data from the io.ReadWriteCloser.
|
||||
//
|
||||
// If the protocol hasn't yet been negotiated, this method triggers the write
|
||||
// half of the handshake and then waits for the read half to complete.
|
||||
//
|
||||
// It returns an error if the read half of the handshake fails.
|
||||
func (l *lazyClientConn) Read(b []byte) (int, error) {
|
||||
l.rhandshakeOnce.Do(func() {
|
||||
go l.whandshakeOnce.Do(l.doWriteHandshake)
|
||||
l.doReadHandshake()
|
||||
})
|
||||
if l.rerr != nil {
|
||||
return 0, l.rerr
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return l.con.Read(b)
|
||||
}
|
||||
|
||||
func (l *lazyClientConn) doReadHandshake() {
|
||||
for _, proto := range l.protos {
|
||||
// read protocol
|
||||
tok, err := ReadNextToken(l.con)
|
||||
if err != nil {
|
||||
l.rerr = err
|
||||
return
|
||||
}
|
||||
|
||||
if tok != proto {
|
||||
l.rerr = fmt.Errorf("protocol mismatch in lazy handshake ( %s != %s )", tok, proto)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lazyClientConn) doWriteHandshake() {
|
||||
l.doWriteHandshakeWithData(nil)
|
||||
}
|
||||
|
||||
// Perform the write handshake but *also* write some extra data.
|
||||
func (l *lazyClientConn) doWriteHandshakeWithData(extra []byte) int {
|
||||
buf := bufio.NewWriter(l.con)
|
||||
for _, proto := range l.protos {
|
||||
l.werr = delimWrite(buf, []byte(proto))
|
||||
if l.werr != nil {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
n := 0
|
||||
if len(extra) > 0 {
|
||||
n, l.werr = buf.Write(extra)
|
||||
if l.werr != nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
l.werr = buf.Flush()
|
||||
return n
|
||||
}
|
||||
|
||||
// Write writes the given buffer to the underlying connection.
|
||||
//
|
||||
// If the protocol has not yet been negotiated, write waits for the write half
|
||||
// of the handshake to complete triggers (but does not wait for) the read half.
|
||||
//
|
||||
// Write *also* ignores errors from the read half of the handshake (in case the
|
||||
// stream is actually write only).
|
||||
func (l *lazyClientConn) Write(b []byte) (int, error) {
|
||||
n := 0
|
||||
l.whandshakeOnce.Do(func() {
|
||||
go l.rhandshakeOnce.Do(l.doReadHandshake)
|
||||
n = l.doWriteHandshakeWithData(b)
|
||||
})
|
||||
if l.werr != nil || n > 0 {
|
||||
return n, l.werr
|
||||
}
|
||||
return l.con.Write(b)
|
||||
}
|
||||
|
||||
// Close closes the underlying io.ReadWriteCloser
|
||||
func (l *lazyClientConn) Close() error {
|
||||
return l.con.Close()
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// lazyServerConn is an io.ReadWriteCloser adapter used for negotiating inbound
|
||||
// streams (see NegotiateLazy).
|
||||
//
|
||||
// This is "lazy" because it doesn't wait for the write half to succeed before
|
||||
// allowing us to read from the stream.
|
||||
type lazyServerConn struct {
|
||||
waitForHandshake sync.Once
|
||||
werr error
|
||||
|
||||
con io.ReadWriteCloser
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Write(b []byte) (int, error) {
|
||||
l.waitForHandshake.Do(func() { panic("didn't initiate handshake") })
|
||||
if l.werr != nil {
|
||||
return 0, l.werr
|
||||
}
|
||||
return l.con.Write(b)
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return l.con.Read(b)
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Close() error {
|
||||
return l.con.Close()
|
||||
}
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
// Package multistream implements a simple stream router for the
|
||||
// multistream-select protocoli. The protocol is defined at
|
||||
// https://github.com/multiformats/multistream-select
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrTooLarge is an error to signal that an incoming message was too large
|
||||
var ErrTooLarge = errors.New("incoming message was too large")
|
||||
|
||||
// ProtocolID identifies the multistream protocol itself and makes sure
|
||||
// the multistream muxers on both sides of a channel can work with each other.
|
||||
const ProtocolID = "/multistream/1.0.0"
|
||||
|
||||
// HandlerFunc is a user-provided function used by the MultistreamMuxer to
|
||||
// handle a protocol/stream.
|
||||
type HandlerFunc func(protocol string, rwc io.ReadWriteCloser) error
|
||||
|
||||
// Handler is a wrapper to HandlerFunc which attaches a name (protocol) and a
|
||||
// match function which can optionally be used to select a handler by other
|
||||
// means than the name.
|
||||
type Handler struct {
|
||||
MatchFunc func(string) bool
|
||||
Handle HandlerFunc
|
||||
AddName string
|
||||
}
|
||||
|
||||
// MultistreamMuxer is a muxer for multistream. Depending on the stream
|
||||
// protocol tag it will select the right handler and hand the stream off to it.
|
||||
type MultistreamMuxer struct {
|
||||
handlerlock sync.Mutex
|
||||
handlers []Handler
|
||||
}
|
||||
|
||||
// NewMultistreamMuxer creates a muxer.
|
||||
func NewMultistreamMuxer() *MultistreamMuxer {
|
||||
return new(MultistreamMuxer)
|
||||
}
|
||||
|
||||
func writeUvarint(w io.Writer, i uint64) error {
|
||||
varintbuf := make([]byte, 16)
|
||||
n := binary.PutUvarint(varintbuf, i)
|
||||
_, err := w.Write(varintbuf[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func delimWriteBuffered(w io.Writer, mes []byte) error {
|
||||
bw := bufio.NewWriter(w)
|
||||
err := delimWrite(bw, mes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bw.Flush()
|
||||
}
|
||||
|
||||
func delimWrite(w io.Writer, mes []byte) error {
|
||||
err := writeUvarint(w, uint64(len(mes)+1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(mes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ls is a Multistream muxer command which returns the list of handler names
|
||||
// available on a muxer.
|
||||
func Ls(rw io.ReadWriter) ([]string, error) {
|
||||
err := delimWriteBuffered(rw, []byte("ls"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := binary.ReadUvarint(&byteReader{rw})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []string
|
||||
for i := uint64(0); i < n; i++ {
|
||||
val, err := lpReadBuf(rw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, string(val))
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func fulltextMatch(s string) func(string) bool {
|
||||
return func(a string) bool {
|
||||
return a == s
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler attaches a new protocol handler to the muxer.
|
||||
func (msm *MultistreamMuxer) AddHandler(protocol string, handler HandlerFunc) {
|
||||
msm.AddHandlerWithFunc(protocol, fulltextMatch(protocol), handler)
|
||||
}
|
||||
|
||||
// AddHandlerWithFunc attaches a new protocol handler to the muxer with a match.
|
||||
// If the match function returns true for a given protocol tag, the protocol
|
||||
// will be selected even if the handler name and protocol tags are different.
|
||||
func (msm *MultistreamMuxer) AddHandlerWithFunc(protocol string, match func(string) bool, handler HandlerFunc) {
|
||||
msm.handlerlock.Lock()
|
||||
msm.removeHandler(protocol)
|
||||
msm.handlers = append(msm.handlers, Handler{
|
||||
MatchFunc: match,
|
||||
Handle: handler,
|
||||
AddName: protocol,
|
||||
})
|
||||
msm.handlerlock.Unlock()
|
||||
}
|
||||
|
||||
// RemoveHandler removes the handler with the given name from the muxer.
|
||||
func (msm *MultistreamMuxer) RemoveHandler(protocol string) {
|
||||
msm.handlerlock.Lock()
|
||||
defer msm.handlerlock.Unlock()
|
||||
|
||||
msm.removeHandler(protocol)
|
||||
}
|
||||
|
||||
func (msm *MultistreamMuxer) removeHandler(protocol string) {
|
||||
for i, h := range msm.handlers {
|
||||
if h.AddName == protocol {
|
||||
msm.handlers = append(msm.handlers[:i], msm.handlers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Protocols returns the list of handler-names added to this this muxer.
|
||||
func (msm *MultistreamMuxer) Protocols() []string {
|
||||
var out []string
|
||||
msm.handlerlock.Lock()
|
||||
for _, h := range msm.handlers {
|
||||
out = append(out, h.AddName)
|
||||
}
|
||||
msm.handlerlock.Unlock()
|
||||
return out
|
||||
}
|
||||
|
||||
// ErrIncorrectVersion is an error reported when the muxer protocol negotiation
|
||||
// fails because of a ProtocolID mismatch.
|
||||
var ErrIncorrectVersion = errors.New("client connected with incorrect version")
|
||||
|
||||
func (msm *MultistreamMuxer) findHandler(proto string) *Handler {
|
||||
msm.handlerlock.Lock()
|
||||
defer msm.handlerlock.Unlock()
|
||||
|
||||
for _, h := range msm.handlers {
|
||||
if h.MatchFunc(proto) {
|
||||
return &h
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NegotiateLazy performs protocol selection and returns
|
||||
// a multistream, the protocol used, the handler and an error. It is lazy
|
||||
// because the write-handshake is performed on a subroutine, allowing this
|
||||
// to return before that handshake is completed.
|
||||
func (msm *MultistreamMuxer) NegotiateLazy(rwc io.ReadWriteCloser) (Multistream, string, HandlerFunc, error) {
|
||||
pval := make(chan string, 1)
|
||||
writeErr := make(chan error, 1)
|
||||
defer close(pval)
|
||||
|
||||
lzc := &lazyServerConn{
|
||||
con: rwc,
|
||||
}
|
||||
|
||||
started := make(chan struct{})
|
||||
go lzc.waitForHandshake.Do(func() {
|
||||
close(started)
|
||||
|
||||
defer close(writeErr)
|
||||
|
||||
if err := delimWriteBuffered(rwc, []byte(ProtocolID)); err != nil {
|
||||
lzc.werr = err
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
|
||||
for proto := range pval {
|
||||
if err := delimWriteBuffered(rwc, []byte(proto)); err != nil {
|
||||
lzc.werr = err
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
<-started
|
||||
|
||||
line, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
if line != ProtocolID {
|
||||
rwc.Close()
|
||||
return nil, "", nil, ErrIncorrectVersion
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
// Now read and respond to commands until they send a valid protocol id
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case "ls":
|
||||
select {
|
||||
case pval <- "ls":
|
||||
case err := <-writeErr:
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
default:
|
||||
h := msm.findHandler(tok)
|
||||
if h == nil {
|
||||
select {
|
||||
case pval <- "na":
|
||||
case err := <-writeErr:
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
|
||||
select {
|
||||
case pval <- tok:
|
||||
case <-writeErr:
|
||||
// explicitly ignore this error. It will be returned to any
|
||||
// writers and if we don't plan on writing anything, we still
|
||||
// want to complete the handshake
|
||||
}
|
||||
|
||||
// hand off processing to the sub-protocol handler
|
||||
return lzc, tok, h.Handle, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Negotiate performs protocol selection and returns the protocol name and
|
||||
// the matching handler function for it (or an error).
|
||||
func (msm *MultistreamMuxer) Negotiate(rwc io.ReadWriteCloser) (string, HandlerFunc, error) {
|
||||
// Send our protocol ID
|
||||
err := delimWriteBuffered(rwc, []byte(ProtocolID))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
line, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if line != ProtocolID {
|
||||
rwc.Close()
|
||||
return "", nil, ErrIncorrectVersion
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
// Now read and respond to commands until they send a valid protocol id
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case "ls":
|
||||
err := msm.Ls(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
default:
|
||||
h := msm.findHandler(tok)
|
||||
if h == nil {
|
||||
err := delimWriteBuffered(rwc, []byte("na"))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
|
||||
err := delimWriteBuffered(rwc, []byte(tok))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// hand off processing to the sub-protocol handler
|
||||
return tok, h.Handle, nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ls implements the "ls" command which writes the list of
|
||||
// supported protocols to the given Writer.
|
||||
func (msm *MultistreamMuxer) Ls(w io.Writer) error {
|
||||
buf := new(bytes.Buffer)
|
||||
msm.handlerlock.Lock()
|
||||
err := writeUvarint(buf, uint64(len(msm.handlers)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range msm.handlers {
|
||||
err := delimWrite(buf, []byte(h.AddName))
|
||||
if err != nil {
|
||||
msm.handlerlock.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
msm.handlerlock.Unlock()
|
||||
ll := make([]byte, 16)
|
||||
nw := binary.PutUvarint(ll, uint64(buf.Len()))
|
||||
|
||||
r := io.MultiReader(bytes.NewReader(ll[:nw]), buf)
|
||||
|
||||
_, err = io.Copy(w, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle performs protocol negotiation on a ReadWriteCloser
|
||||
// (i.e. a connection). It will find a matching handler for the
|
||||
// incoming protocol and pass the ReadWriteCloser to it.
|
||||
func (msm *MultistreamMuxer) Handle(rwc io.ReadWriteCloser) error {
|
||||
p, h, err := msm.Negotiate(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h(p, rwc)
|
||||
}
|
||||
|
||||
// ReadNextToken extracts a token from a ReadWriter. It is used during
|
||||
// protocol negotiation and returns a string.
|
||||
func ReadNextToken(rw io.ReadWriter) (string, error) {
|
||||
tok, err := ReadNextTokenBytes(rw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(tok), nil
|
||||
}
|
||||
|
||||
// ReadNextTokenBytes extracts a token from a ReadWriter. It is used
|
||||
// during protocol negotiation and returns a byte slice.
|
||||
func ReadNextTokenBytes(rw io.ReadWriter) ([]byte, error) {
|
||||
data, err := lpReadBuf(rw)
|
||||
switch err {
|
||||
case nil:
|
||||
return data, nil
|
||||
case ErrTooLarge:
|
||||
err := delimWriteBuffered(rw, []byte("messages over 64k are not allowed"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrTooLarge
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func lpReadBuf(r io.Reader) ([]byte, error) {
|
||||
br, ok := r.(io.ByteReader)
|
||||
if !ok {
|
||||
br = &byteReader{r}
|
||||
}
|
||||
|
||||
length, err := binary.ReadUvarint(br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length > 64*1024 {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
buf := make([]byte, length)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(buf) == 0 || buf[length-1] != '\n' {
|
||||
return nil, errors.New("message did not have trailing newline")
|
||||
}
|
||||
|
||||
// slice off the trailing newline
|
||||
buf = buf[:length-1]
|
||||
|
||||
return buf, nil
|
||||
|
||||
}
|
||||
|
||||
// byteReader implements the ByteReader interface that ReadUVarint requires
|
||||
type byteReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (br *byteReader) ReadByte() (byte, error) {
|
||||
var b [1]byte
|
||||
n, err := br.Read(b[:])
|
||||
if n == 1 {
|
||||
return b[0], nil
|
||||
}
|
||||
if err == nil {
|
||||
if n != 0 {
|
||||
panic("read more bytes than buffer size")
|
||||
}
|
||||
err = io.ErrNoProgress
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user