Merge pull request #148 from mrd0ll4r/deniability-middleware
Deniability middleware, some util packages
This commit is contained in:
commit
83ffc0425e
10 changed files with 553 additions and 0 deletions
|
@ -21,6 +21,7 @@ import (
|
||||||
_ "github.com/chihaya/chihaya/server/store/memory"
|
_ "github.com/chihaya/chihaya/server/store/memory"
|
||||||
_ "github.com/chihaya/chihaya/server/store/middleware/client"
|
_ "github.com/chihaya/chihaya/server/store/middleware/client"
|
||||||
_ "github.com/chihaya/chihaya/server/store/middleware/ip"
|
_ "github.com/chihaya/chihaya/server/store/middleware/ip"
|
||||||
|
_ "github.com/chihaya/chihaya/middleware/deniability"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configPath string
|
var configPath string
|
||||||
|
|
39
middleware/deniability/README.md
Normal file
39
middleware/deniability/README.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
## Deniability Middleware
|
||||||
|
|
||||||
|
This package provides the announce middleware `deniability` which inserts ghost peers into announce responses to achieve plausible deniability.
|
||||||
|
|
||||||
|
### Functionality
|
||||||
|
|
||||||
|
This middleware will choose random announces and modify the list of peers returned.
|
||||||
|
A random number of randomly generated peers will be inserted at random positions into the list of peers.
|
||||||
|
As soon as the list of peers exceeds `numWant`, peers will be replaced rather than inserted.
|
||||||
|
|
||||||
|
Note that if a response is picked for augmentation, both IPv4 and IPv6 peers will be modified, in case they are not empty.
|
||||||
|
|
||||||
|
Also note that the IP address for the generated peeer consists of bytes in the range [1,254].
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
This middleware provides the following parameters for configuration:
|
||||||
|
|
||||||
|
- `modify_response_probability` (float, >0, <= 1) indicates the probability by which a response will be augmented with random peers.
|
||||||
|
- `max_random_peers` (int, >0) sets an upper boundary (inclusive) for the amount of peers added.
|
||||||
|
- `prefix` (string, 20 characters at most) sets the prefix for generated peer IDs.
|
||||||
|
The peer ID will be padded to 20 bytes using a random string of alphanumeric characters.
|
||||||
|
- `min_port` (int, >0, <=65535) sets a lower boundary for the port for generated peers.
|
||||||
|
- `max_port` (int, >0, <=65536, > `min_port`) sets an upper boundary for the port for generated peers.
|
||||||
|
|
||||||
|
An example config might look like this:
|
||||||
|
|
||||||
|
chihaya:
|
||||||
|
tracker:
|
||||||
|
announce_middleware:
|
||||||
|
- name: deniability
|
||||||
|
config:
|
||||||
|
modify_response_probability: 0.2
|
||||||
|
max_random_peers: 5
|
||||||
|
prefix: -AZ2060-
|
||||||
|
min_port: 40000
|
||||||
|
max_port: 60000
|
||||||
|
|
||||||
|
For more information about peer IDs and their prefixes, see [this wiki entry](https://wiki.theory.org/BitTorrentSpecification#peer_id).
|
46
middleware/deniability/config.go
Normal file
46
middleware/deniability/config.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package deniability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/chihaya/chihaya"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents the configuration for the deniability middleware.
|
||||||
|
type Config struct {
|
||||||
|
// ModifyResponseProbability is the probability by which a response will
|
||||||
|
// be augmented with random peers.
|
||||||
|
ModifyResponseProbability float32 `yaml:"modify_response_probability"`
|
||||||
|
|
||||||
|
// MaxRandomPeers is the amount of peers that will be added at most.
|
||||||
|
MaxRandomPeers int `yaml:"max_random_peers"`
|
||||||
|
|
||||||
|
// Prefix is the prefix to be used for peer IDs.
|
||||||
|
Prefix string `yaml:"prefix"`
|
||||||
|
|
||||||
|
// MinPort is the minimum port (inclusive) for the generated peer.
|
||||||
|
MinPort int `yaml:"min_port"`
|
||||||
|
|
||||||
|
// MaxPort is the maximum port (exclusive) for the generated peer.
|
||||||
|
MaxPort int `yaml:"max_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConfig parses the given MiddlewareConfig as a deniability.Config.
|
||||||
|
func newConfig(mwcfg chihaya.MiddlewareConfig) (*Config, error) {
|
||||||
|
bytes, err := yaml.Marshal(mwcfg.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg Config
|
||||||
|
err = yaml.Unmarshal(bytes, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
63
middleware/deniability/config_test.go
Normal file
63
middleware/deniability/config_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package deniability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/chihaya/chihaya"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configTestData struct {
|
||||||
|
modifyProbability string
|
||||||
|
maxNewPeers string
|
||||||
|
prefix string
|
||||||
|
minPort string
|
||||||
|
maxPort string
|
||||||
|
err bool
|
||||||
|
expected Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
configTemplate = `
|
||||||
|
name: foo
|
||||||
|
config:
|
||||||
|
modify_response_probability: %s
|
||||||
|
max_random_peers: %s
|
||||||
|
prefix: %s
|
||||||
|
min_port: %s
|
||||||
|
max_port: %s`
|
||||||
|
|
||||||
|
configData = []configTestData{
|
||||||
|
{"1.0", "5", "abc", "2000", "3000", false, Config{1.0, 5, "abc", 2000, 3000}},
|
||||||
|
{"a", "a", "12", "a", "a", true, Config{}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewConfig(t *testing.T) {
|
||||||
|
var mwconfig chihaya.MiddlewareConfig
|
||||||
|
|
||||||
|
cfg, err := newConfig(mwconfig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
|
||||||
|
for _, test := range configData {
|
||||||
|
config := fmt.Sprintf(configTemplate, test.modifyProbability, test.maxNewPeers, test.prefix, test.minPort, test.maxPort)
|
||||||
|
err = yaml.Unmarshal([]byte(config), &mwconfig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
cfg, err = newConfig(mwconfig)
|
||||||
|
if test.err {
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, test.expected, *cfg)
|
||||||
|
}
|
||||||
|
}
|
121
middleware/deniability/deniability.go
Normal file
121
middleware/deniability/deniability.go
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package deniability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/chihaya/chihaya"
|
||||||
|
"github.com/chihaya/chihaya/pkg/random"
|
||||||
|
"github.com/chihaya/chihaya/tracker"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tracker.RegisterAnnounceMiddlewareConstructor("deniability", constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
type deniabilityMiddleware struct {
|
||||||
|
cfg *Config
|
||||||
|
r *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor provides a middleware constructor that returns a middleware to
|
||||||
|
// insert peers into the peer lists returned as a response to an announce.
|
||||||
|
//
|
||||||
|
// It returns an error if the config provided is either syntactically or
|
||||||
|
// semantically incorrect.
|
||||||
|
func constructor(c chihaya.MiddlewareConfig) (tracker.AnnounceMiddleware, error) {
|
||||||
|
cfg, err := newConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ModifyResponseProbability <= 0 || cfg.ModifyResponseProbability > 1 {
|
||||||
|
return nil, errors.New("modify_response_probability must be in [0,1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.MaxRandomPeers <= 0 {
|
||||||
|
return nil, errors.New("max_random_peers must be > 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.MinPort <= 0 {
|
||||||
|
return nil, errors.New("min_port must not be <= 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.MaxPort > 65536 {
|
||||||
|
return nil, errors.New("max_port must not be > 65536")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.MinPort >= cfg.MaxPort {
|
||||||
|
return nil, errors.New("max_port must not be <= min_port")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.Prefix) > 20 {
|
||||||
|
return nil, errors.New("prefix must not be longer than 20 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
mw := deniabilityMiddleware{
|
||||||
|
cfg: cfg,
|
||||||
|
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||||
|
}
|
||||||
|
|
||||||
|
return mw.modifyResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *deniabilityMiddleware) modifyResponse(next tracker.AnnounceHandler) tracker.AnnounceHandler {
|
||||||
|
return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error {
|
||||||
|
err := next(cfg, req, resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mw.cfg.ModifyResponseProbability == 1 || mw.r.Float32() < mw.cfg.ModifyResponseProbability {
|
||||||
|
numNewPeers := mw.r.Intn(mw.cfg.MaxRandomPeers) + 1
|
||||||
|
for i := 0; i < numNewPeers; i++ {
|
||||||
|
if len(resp.IPv6Peers) > 0 {
|
||||||
|
if len(resp.IPv6Peers) >= int(req.NumWant) {
|
||||||
|
mw.replacePeer(resp.IPv6Peers, true)
|
||||||
|
} else {
|
||||||
|
resp.IPv6Peers = mw.insertPeer(resp.IPv6Peers, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.IPv4Peers) > 0 {
|
||||||
|
if len(resp.IPv4Peers) >= int(req.NumWant) {
|
||||||
|
mw.replacePeer(resp.IPv4Peers, false)
|
||||||
|
} else {
|
||||||
|
resp.IPv4Peers = mw.insertPeer(resp.IPv4Peers, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacePeer replaces a peer from a random position within the given slice
|
||||||
|
// of peers with a randomly generated one.
|
||||||
|
//
|
||||||
|
// replacePeer panics if len(peers) == 0.
|
||||||
|
func (mw *deniabilityMiddleware) replacePeer(peers []chihaya.Peer, v6 bool) {
|
||||||
|
peers[mw.r.Intn(len(peers))] = random.Peer(mw.r, mw.cfg.Prefix, v6, mw.cfg.MinPort, mw.cfg.MaxPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertPeer inserts a randomly generated peer at a random position into the
|
||||||
|
// given slice and returns the new slice.
|
||||||
|
func (mw *deniabilityMiddleware) insertPeer(peers []chihaya.Peer, v6 bool) []chihaya.Peer {
|
||||||
|
pos := 0
|
||||||
|
if len(peers) > 0 {
|
||||||
|
pos = mw.r.Intn(len(peers))
|
||||||
|
}
|
||||||
|
peers = append(peers, chihaya.Peer{})
|
||||||
|
copy(peers[pos+1:], peers[pos:])
|
||||||
|
peers[pos] = random.Peer(mw.r, mw.cfg.Prefix, v6, mw.cfg.MinPort, mw.cfg.MaxPort)
|
||||||
|
|
||||||
|
return peers
|
||||||
|
}
|
110
middleware/deniability/deniability_test.go
Normal file
110
middleware/deniability/deniability_test.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package deniability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/chihaya/chihaya"
|
||||||
|
)
|
||||||
|
|
||||||
|
type constructorTestData struct {
|
||||||
|
cfg Config
|
||||||
|
error bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructorData = []constructorTestData{
|
||||||
|
{Config{1.0, 10, "abc", 1024, 1025}, false},
|
||||||
|
{Config{1.1, 10, "abc", 1024, 1025}, true},
|
||||||
|
{Config{0, 10, "abc", 1024, 1025}, true},
|
||||||
|
{Config{1.0, 0, "abc", 1024, 1025}, true},
|
||||||
|
{Config{1.0, 10, "01234567890123456789_", 1024, 1025}, true},
|
||||||
|
{Config{1.0, 10, "abc", 0, 1025}, true},
|
||||||
|
{Config{1.0, 10, "abc", 1024, 0}, true},
|
||||||
|
{Config{1.0, 10, "abc", 1024, 65537}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplacePeer(t *testing.T) {
|
||||||
|
cfg := Config{
|
||||||
|
Prefix: "abc",
|
||||||
|
MinPort: 1024,
|
||||||
|
MaxPort: 1025,
|
||||||
|
}
|
||||||
|
mw := deniabilityMiddleware{
|
||||||
|
r: rand.New(rand.NewSource(0)),
|
||||||
|
cfg: &cfg,
|
||||||
|
}
|
||||||
|
peer := chihaya.Peer{
|
||||||
|
ID: chihaya.PeerID("abcdefghijklmnoprstu"),
|
||||||
|
Port: 2000,
|
||||||
|
IP: net.ParseIP("10.150.255.23"),
|
||||||
|
}
|
||||||
|
peers := []chihaya.Peer{peer}
|
||||||
|
|
||||||
|
mw.replacePeer(peers, false)
|
||||||
|
assert.Equal(t, 1, len(peers))
|
||||||
|
assert.Equal(t, "abc", string(peers[0].ID[:3]))
|
||||||
|
assert.Equal(t, uint16(1024), peers[0].Port)
|
||||||
|
assert.NotNil(t, peers[0].IP.To4())
|
||||||
|
|
||||||
|
mw.replacePeer(peers, true)
|
||||||
|
assert.Equal(t, 1, len(peers))
|
||||||
|
assert.Equal(t, "abc", string(peers[0].ID[:3]))
|
||||||
|
assert.Equal(t, uint16(1024), peers[0].Port)
|
||||||
|
assert.Nil(t, peers[0].IP.To4())
|
||||||
|
|
||||||
|
peers = []chihaya.Peer{peer, peer}
|
||||||
|
|
||||||
|
mw.replacePeer(peers, true)
|
||||||
|
assert.True(t, (peers[0].Port == peer.Port) != (peers[1].Port == peer.Port), "not exactly one peer was replaced")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertPeer(t *testing.T) {
|
||||||
|
cfg := Config{
|
||||||
|
Prefix: "abc",
|
||||||
|
MinPort: 1024,
|
||||||
|
MaxPort: 1025,
|
||||||
|
}
|
||||||
|
mw := deniabilityMiddleware{
|
||||||
|
r: rand.New(rand.NewSource(0)),
|
||||||
|
cfg: &cfg,
|
||||||
|
}
|
||||||
|
peer := chihaya.Peer{
|
||||||
|
ID: chihaya.PeerID("abcdefghijklmnoprstu"),
|
||||||
|
Port: 2000,
|
||||||
|
IP: net.ParseIP("10.150.255.23"),
|
||||||
|
}
|
||||||
|
var peers []chihaya.Peer
|
||||||
|
|
||||||
|
peers = mw.insertPeer(peers, false)
|
||||||
|
assert.Equal(t, 1, len(peers))
|
||||||
|
assert.Equal(t, uint16(1024), peers[0].Port)
|
||||||
|
assert.Equal(t, "abc", string(peers[0].ID[:3]))
|
||||||
|
assert.NotNil(t, peers[0].IP.To4())
|
||||||
|
|
||||||
|
peers = []chihaya.Peer{peer, peer}
|
||||||
|
|
||||||
|
peers = mw.insertPeer(peers, true)
|
||||||
|
assert.Equal(t, 3, len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstructor(t *testing.T) {
|
||||||
|
for _, tt := range constructorData {
|
||||||
|
_, err := constructor(chihaya.MiddlewareConfig{
|
||||||
|
Config: tt.cfg,
|
||||||
|
})
|
||||||
|
|
||||||
|
if tt.error {
|
||||||
|
assert.NotNil(t, err, fmt.Sprintf("error expected for %+v", tt.cfg))
|
||||||
|
} else {
|
||||||
|
assert.Nil(t, err, fmt.Sprintf("no error expected for %+v", tt.cfg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
pkg/random/peer.go
Normal file
74
pkg/random/peer.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package random
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/chihaya/chihaya"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peer generates a random chihaya.Peer.
|
||||||
|
//
|
||||||
|
// prefix is the prefix to use for the peer ID. If len(prefix) > 20, it will be
|
||||||
|
// truncated to 20 characters. If len(prefix) < 20, it will be padded with an
|
||||||
|
// alphanumeric random string to have 20 characters.
|
||||||
|
//
|
||||||
|
// v6 indicates whether an IPv6 address should be generated.
|
||||||
|
// Regardless of the length of the generated IP address, its bytes will have
|
||||||
|
// values in [1,254].
|
||||||
|
//
|
||||||
|
// minPort and maxPort describe the range for the randomly generated port, where
|
||||||
|
// minPort <= port < maxPort.
|
||||||
|
// minPort and maxPort will be checked and altered so that
|
||||||
|
// 1 <= minPort <= maxPort <= 65536.
|
||||||
|
// If minPort == maxPort, port will be set to minPort.
|
||||||
|
func Peer(r *rand.Rand, prefix string, v6 bool, minPort, maxPort int) chihaya.Peer {
|
||||||
|
var (
|
||||||
|
port uint16
|
||||||
|
ip net.IP
|
||||||
|
)
|
||||||
|
|
||||||
|
if minPort <= 0 {
|
||||||
|
minPort = 1
|
||||||
|
}
|
||||||
|
if maxPort > 65536 {
|
||||||
|
maxPort = 65536
|
||||||
|
}
|
||||||
|
if maxPort < minPort {
|
||||||
|
maxPort = minPort
|
||||||
|
}
|
||||||
|
if len(prefix) > 20 {
|
||||||
|
prefix = prefix[:20]
|
||||||
|
}
|
||||||
|
|
||||||
|
if minPort == maxPort {
|
||||||
|
port = uint16(minPort)
|
||||||
|
} else {
|
||||||
|
port = uint16(r.Int63()%int64(maxPort-minPort)) + uint16(minPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v6 {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
ip = net.IP(b)
|
||||||
|
} else {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
ip = net.IP(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range ip {
|
||||||
|
b := r.Intn(254) + 1
|
||||||
|
ip[i] = byte(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix = prefix + AlphaNumericString(r, 20-len(prefix))
|
||||||
|
|
||||||
|
return chihaya.Peer{
|
||||||
|
ID: chihaya.PeerID(prefix),
|
||||||
|
Port: port,
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
|
}
|
43
pkg/random/peer_test.go
Normal file
43
pkg/random/peer_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package random
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPeer(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(0))
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
minPort := 2000
|
||||||
|
maxPort := 2010
|
||||||
|
p := Peer(r, "", false, minPort, maxPort)
|
||||||
|
assert.Equal(t, 20, len(p.ID))
|
||||||
|
assert.True(t, p.Port >= uint16(minPort) && p.Port < uint16(maxPort))
|
||||||
|
assert.NotNil(t, p.IP.To4())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
minPort := 2000
|
||||||
|
maxPort := 2010
|
||||||
|
p := Peer(r, "", true, minPort, maxPort)
|
||||||
|
assert.Equal(t, 20, len(p.ID))
|
||||||
|
assert.True(t, p.Port >= uint16(minPort) && p.Port < uint16(maxPort))
|
||||||
|
assert.True(t, len(p.IP) == net.IPv6len)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Peer(r, "abcdefghijklmnopqrst", false, 2000, 2000)
|
||||||
|
assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID))
|
||||||
|
assert.Equal(t, uint16(2000), p.Port)
|
||||||
|
|
||||||
|
p = Peer(r, "abcdefghijklmnopqrstUVWXYZ", true, -10, -5)
|
||||||
|
assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID))
|
||||||
|
assert.True(t, p.Port >= uint16(1) && p.Port <= uint16(65535))
|
||||||
|
}
|
26
pkg/random/string.go
Normal file
26
pkg/random/string.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package random
|
||||||
|
|
||||||
|
import "math/rand"
|
||||||
|
|
||||||
|
// AlphaNumeric is an alphabet with all lower- and uppercase letters and
|
||||||
|
// numbers.
|
||||||
|
const AlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
// AlphaNumericString is a shorthand for String(r, l, AlphaNumeric).
|
||||||
|
func AlphaNumericString(r rand.Source, l int) string {
|
||||||
|
return String(r, l, AlphaNumeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String generates a random string of length l, containing only runes from
|
||||||
|
// the alphabet using the random source r.
|
||||||
|
func String(r rand.Source, l int, alphabet string) string {
|
||||||
|
b := make([]byte, l)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = alphabet[r.Int63()%int64(len(alphabet))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
30
pkg/random/string_test.go
Normal file
30
pkg/random/string_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the BSD 2-Clause license,
|
||||||
|
// which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package random
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAlphaNumericString(t *testing.T) {
|
||||||
|
r := rand.NewSource(0)
|
||||||
|
|
||||||
|
s := AlphaNumericString(r, 0)
|
||||||
|
assert.Equal(t, 0, len(s))
|
||||||
|
|
||||||
|
s = AlphaNumericString(r, 10)
|
||||||
|
assert.Equal(t, 10, len(s))
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s := AlphaNumericString(r, 10)
|
||||||
|
for _, c := range s {
|
||||||
|
assert.True(t, strings.Contains(AlphaNumeric, string(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue