Initial commit

This commit is contained in:
Kalle Vedin
2014-06-26 02:35:25 +02:00
commit 922fe8d996
9 changed files with 890 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~
*.swp

91
I2PAddr.go Normal file
View File

@ -0,0 +1,91 @@
package sam3
import (
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"errors"
)
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
// Base64 representation of the private keys
type I2PKeys struct {
addr I2PAddr
priv string
}
// Returns the string of I2PKeys (Base64-encoded)
func (k I2PKeys) String() string {
return k.priv
}
// Returns the I2PAddr (I2P destination) belonging to the I2PKeys.
func (k I2PKeys) Addr() I2PAddr {
return k.addr
}
// Base64 representation of the public keys
type I2PAddr string
// Returns the base64 representation of the I2PAddr
func (a I2PAddr) Base64() string {
return string(a)
}
// Returns the I2P destination (base64-encoded)
func (a I2PAddr) String() string {
return string(a)
}
// Returns "I2P"
func (a I2PAddr) Network() string {
return "I2P"
}
// Creates a new I2P address from a base64-encoded string.
func NewI2PAddrFromString(addr string) (I2PAddr, error) {
// very basic check
if len(addr) > 4096 || len(addr) < 516 {
return I2PAddr(""), errors.New("Not an I2P address")
}
buf := make([]byte, 4096)
if _, err := i2pB64enc.Decode(buf, []byte(addr)); err != nil {
return I2PAddr(""), errors.New("Address is not base64-encoded")
}
return I2PAddr(addr), nil
}
// Creates a new I2P address from a byte array (pure binary, as you would have
// if you have *not* base64-encoded it.)
func NewI2PAddrFromBytes(addr []byte) (I2PAddr, error) {
if len(addr) > 4096 || len(addr) < 384 {
return I2PAddr(""), errors.New("Not an I2P address")
}
buf := make([]byte, i2pB64enc.EncodedLen(len(addr)))
i2pB64enc.Encode(buf, addr)
return I2PAddr(string(buf)), nil
}
// Returns the *.b32.i2p address of the I2P address.
func (addr I2PAddr) Base32() string {
hash := sha256.New()
hash.Write([]byte(string(addr)))
digest := hash.Sum(nil)
b32addr := make([]byte, 56)
i2pB32enc.Encode(b32addr, digest)
return string(b32addr[:52]) + ".b32.i2p"
}
// Shortcut to I2PAddr.Base32()
func Base32(addr I2PAddr) string {
return addr.Base32()
}

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# README #
go library for the I2P SAMv3 bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
## Support/TODO ##
**What works:**
* Utils
* Resolving URLs to I2P destinations
* .b32.i2p hashes
* Generating keys/i2p destinations
* Streaming
* DialI2P() - Connecting to stuff in I2P
* Listen()/Accept() - Handling incomming connections
**Does not work:**
* Datagram sockets
* Raw sockets
## Documentation ##
* Enter `godoc -http=:8081` into your terminal and hit enter.
* Goto http://localhost:8081, click packages, and navigate to sam3
## Testing ##
* `go test` runs the whole suite
* `go test -short` runs the shorter variant
## License ##
Public domain.
## Author ##
Kalle Vedin `kalle.vedin@fripost.org`

48
SAMConn.go Normal file
View File

@ -0,0 +1,48 @@
package sam3
import (
"time"
"net"
)
type SAMConn struct {
laddr I2PAddr
raddr I2PAddr
conn net.Conn
}
func (sc SAMConn) Read(buf []byte) (int, error) {
n, err := sc.conn.Read(buf)
return n, err
}
func (sc SAMConn) Write(buf []byte) (int, error) {
n, err := sc.conn.Write(buf)
return n, err
}
func (sc SAMConn) Close() error {
return sc.conn.Close()
}
func (sc SAMConn) LocalAddr() I2PAddr {
return sc.laddr
}
func (sc SAMConn) RemoteAddr() I2PAddr {
return sc.raddr
}
func (sc SAMConn) SetDeadline(t time.Time) error {
return sc.conn.SetDeadline(t)
}
func (sc SAMConn) SetReadDeadline(t time.Time) error {
return sc.conn.SetReadDeadline(t)
}
func (sc SAMConn) SetWriteDeadline(t time.Time) error {
return sc.conn.SetWriteDeadline(t)
}

54
datagram.go Normal file
View File

@ -0,0 +1,54 @@
package sam3
import (
"errors"
"net"
"time"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
// UDP, except that datagrams may be much larger (max 31kB). These datagrams are
// also end-to-end encrypted, signed and includes replay-protection. And they
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
Addr I2PAddr
Priv I2PKeys
lport int
}
// Implements net.PacketConn
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
return 0, I2PAddr(""), errors.New("Not implemented.")
}
// Implements net.PacketConn
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return 0, errors.New("Not implemented.")
}
// Implements net.PacketConn
func (s *DatagramSession) Close() error {
return errors.New("Not implemented.")
}
// Implements net.PacketConn
func (s *DatagramSession) LocalAddr() net.Addr {
return I2PAddr("")
}
// Implements net.PacketConn
func (s *DatagramSession) SetDeadline(t time.Time) error {
return errors.New("Not implemented.")
}
// Implements net.PacketConn
func (s *DatagramSession) SetReadDeadline(t time.Time) error {
return errors.New("Not implemented.")
}
// Implements net.PacketConn
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
return errors.New("Not implemented.")
}

190
sam3.go Normal file
View File

@ -0,0 +1,190 @@
package sam3
import (
"bufio"
"bytes"
"net"
"errors"
"strings"
// "fmt"
)
// Used for controlling I2Ps SAMv3.
type SAM struct {
address string // ipv4:port
conn net.Conn
}
const (
session_OK = "SESSION STATUS RESULT=OK DESTINATION="
session_DUPLICATE_ID = "SESSION STATUS RESULT=DUPLICATED_ID\n"
session_DUPLICATE_DEST = "SESSION STATUS RESULT=DUPLICATED_DEST\n"
session_INVALID_KEY = "SESSION STATUS RESULT=INVALID_KEY\n"
session_I2P_ERROR = "SESSION STATUS RESULT=I2P_ERROR MESSAGE="
)
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
conn, err := net.Dial("tcp4", address)
if err != nil {
return nil, err
}
if _, err := conn.Write([]byte("HELLO VERSION MIN=3.0 MAX=3.0\n")); err != nil {
return nil, err
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
return nil, err
}
if string(buf[:n]) == "HELLO REPLY RESULT=OK VERSION=3.0\n" {
return &SAM{address, conn}, nil
} else if string(buf[:n]) == "HELLO REPLY RESULT=NOVERSION\n" {
return nil, errors.New("That SAM bridge does not support SAMv3.")
} else {
return nil, errors.New(string(buf[:n]))
}
}
func (sam *SAM) NewKeys() (I2PAddr, I2PKeys, error) {
if _, err := sam.conn.Write([]byte("DEST GENERATE\n")); err != nil {
return I2PAddr(""), I2PKeys{}, err
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return I2PAddr(""), I2PKeys{}, err
}
s := bufio.NewScanner(bytes.NewReader(buf[:n]))
s.Split(bufio.ScanWords)
var pub, priv string
for s.Scan() {
text := s.Text()
if text == "DEST" {
continue
} else if text == "REPLY" {
continue
} else if strings.HasPrefix(text, "PUB=") {
pub = text[4:]
} else if strings.HasPrefix(text, "PRIV=") {
priv = text[5:]
} else {
return I2PAddr(""), I2PKeys{}, errors.New("Failed to parse keys.")
}
}
return I2PAddr(pub), I2PKeys{I2PAddr(pub), priv}, nil
}
func (sam *SAM) Lookup(name string) (I2PAddr, error) {
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\n")); err != nil {
return I2PAddr(""), err
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return I2PAddr(""), err
}
if n <= 13 || !strings.HasPrefix(string(buf[:n]), "NAMING REPLY ") {
return I2PAddr(""), errors.New("Failed to parse.")
}
s := bufio.NewScanner(bytes.NewReader(buf[13:n]))
s.Split(bufio.ScanWords)
errStr := ""
for s.Scan() {
text := s.Text()
if text == "RESULT=OK" {
continue
} else if text == "RESULT=INVALID_KEY" {
errStr += "Invalid key."
} else if text == "RESULT=KEY_NOT_FOUND" {
errStr += "Unable to resolve " + name
} else if text == "NAME=" + name {
continue
} else if strings.HasPrefix(text, "VALUE=") {
return I2PAddr(text[6:]), nil
} else if strings.HasPrefix(text, "MESSAGE=") {
errStr += " " + text[8:]
} else {
return I2PAddr(""), errors.New("Failed to parse lookup reply.")
}
}
return I2PAddr(""), errors.New(errStr)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Returns the newly created connection
// to the SAMv3 bridge.
func (sam *SAM) newGenericSession(style, id string, keys I2PKeys, options []string) (net.Conn, error) {
sam2, err := NewSAM(sam.address)
if err != nil {
return nil, errors.New("Unable to create new streaming tunnel.")
}
optStr := ""
for _, opt := range options {
optStr += "OPTION=" + opt + " "
}
conn := sam2.conn
scmsg := []byte("SESSION CREATE STYLE=" + style + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + "\n")
for m, i:=0, 0; m!=len(scmsg); i++ {
if i == 15 {
conn.Close()
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
conn.Close()
return nil, err
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
text := string(buf[:n])
if strings.HasPrefix(text, session_OK) {
if keys.String() != text[len(session_OK):len(text)-1] {
return nil, errors.New("SAMv3 created a tunnel with keys other than the ones we asked it for")
}
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == session_DUPLICATE_ID {
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == session_DUPLICATE_DEST {
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
conn.Close()
return nil, errors.New("Invalid key")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
} else {
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
}
func (sam *SAM) NewDatagramSession(tunnelName string, keys I2PKeys, length, variance, backup int) (*DatagramSession, error) {
return nil, errors.New("Not implemented.")
}
func (sam *SAM) Close() error {
if err := sam.conn.Close(); err != nil {
return err
}
return nil
}

230
sam_test.go Normal file
View File

@ -0,0 +1,230 @@
package sam3
import (
"fmt"
"strings"
"testing"
// "time"
)
const yoursam = "127.0.0.1:7656"
func Test_Basic(t *testing.T) {
fmt.Println("Test_Basic")
fmt.Println("\tAttaching to SAM at " + yoursam)
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error)
t.Fail()
return
}
fmt.Println("\tCreating new keys...")
addr, keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
fmt.Println("\tAddress created: " + addr.Base32())
fmt.Println("\tI2PKeys: " + string(keys.priv)[:50] + "(...etc)")
}
addr2, err := sam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
fmt.Println("\tzzz.i2p = " + addr2.Base32())
}
if err := sam.Close(); err != nil {
fmt.Println(err.Error())
t.Fail()
}
}
/*
func Test_GenericSession(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_GenericSession")
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error)
t.Fail()
return
}
_, keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn1, err := sam.newGenericSession("STREAM", "testTun", keys, []string{})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn1.Close()
}
conn2, err := sam.newGenericSession("STREAM", "testTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=1", "outbound.lengthVariance=1", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn2.Close()
}
conn3, err := sam.newGenericSession("DATAGRAM", "testTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=1", "outbound.lengthVariance=1", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn3.Close()
}
}
if err := sam.Close(); err != nil {
fmt.Println(err.Error())
t.Fail()
}
}
*/
func Test_StreamingDial(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingDial")
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error)
t.Fail()
return
}
defer sam.Close()
_, keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tBuilding tunnel")
ss, err := sam.NewStreamSession("streamTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tLooking up forum.i2p")
forumAddr, err := sam.Lookup("forum.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tDialing forum.i2p")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer conn.Close()
fmt.Println("\tSending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("\tProbably failed to StreamSession.DialI2P(forum.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("\tRead HTTP/HTML from forum.i2p")
}
}
func Test_StreamingServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer sam.Close()
_, keys, err := sam.NewKeys()
if err != nil {
t.Fail()
return
}
fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewStreamSession("serverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan(bool)) {
if !(<-w) {
return
}
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
_, keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
ss2, err := sam2.NewStreamSession("clientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connecting to server")
conn, err := ss2.DialI2P(ss.Addr())
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connected to tunnel")
defer conn.Close()
_, err = conn.Write([]byte("Hello world <3 <3 <3 <3 <3 <3"))
if err != nil {
c <- false
return
}
c <- true
}(c, w)
l, err := ss.Listen()
if err != nil {
fmt.Println("ss.Listen(): " + err.Error())
t.Fail()
w <- false
return
}
w <- true
fmt.Println("\tServer: Accept()ing on tunnel")
conn, err := l.Accept()
buf := make([]byte, 512)
n,err := conn.Read(buf)
fmt.Printf("\tClient exited successfully: %t\n", <-c)
fmt.Println("\tServer: received from Client: " + string(buf[:n]))
}

199
stream.go Normal file
View File

@ -0,0 +1,199 @@
package sam3
import (
"bufio"
"bytes"
"errors"
"net"
"strconv"
"strings"
"sync"
)
// Represents a streaming session.
type StreamSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
keys I2PKeys // i2p destination keys
listener *StreamListener // used for accepting inbound calls
l sync.Mutex // lock for this struct
err error
}
// Returns the local tunnel name of the I2P tunnel used to carry the stream session
func (ss StreamSession) ID() string {
return ss.id
}
// Returns the I2P destination (the address) of the stream session
func (ss StreamSession) Addr() I2PAddr {
return ss.keys.Addr()
}
// Returns the cypher keys associated with the stream session
func (ss StreamSession) Keys() I2PKeys {
return ss.keys
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. All other StreamingSession constructors are variants of this one.
func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error) {
conn, err := sam.newGenericSession("STREAM", id, keys, options)
if err != nil {
return nil, err
}
return &StreamSession{sam.address, id, conn, keys, nil, sync.Mutex{}, nil}, nil
}
// Dials to an I2P destination, returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error) {
sam, err := NewSAM(s.samAddr)
if err != nil {
return nil, err
}
conn := sam.conn
_,err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
if err != nil {
return nil, err
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
switch scanner.Text() {
case "STREAM" :
continue
case "STATUS" :
continue
case "RESULT=OK" :
return &SAMConn{s.keys.addr, addr, conn}, nil
case "RESULT=CANT_REACH_PEER" :
return nil, errors.New("Can not reach peer")
case "RESULT=I2P_ERROR" :
return nil, errors.New("I2P internal error")
case "RESULT=INVALID_KEY" :
return nil, errors.New("Invalid key")
case "RESULT=INVALID_ID" :
return nil, errors.New("Invalid tunnel ID")
case "RESULT=TIMEOUT" :
return nil, errors.New("Timeout")
default :
return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
}
}
panic("sam3 go library error in StreamSession.DialI2P()")
}
// Returns a listener for the I2P destination (I2PAddr) associated with the
// StreamSession.
func (s *StreamSession) Listen() (*StreamListener, error) {
sam, err := NewSAM(s.conn.RemoteAddr().String())
if err != nil {
return nil, err
}
listener, err := net.Listen("tcp4", ":0")
_, lport, err := net.SplitHostPort(listener.Addr().String())
if err != nil {
sam.Close()
return nil, err
}
conn := sam.conn
_, err = conn.Write([]byte("STREAM FORWARD ID=" + s.id + " PORT=" + lport + " SILENT=false\n"))
if err != nil {
conn.Close()
return nil, err
}
buf := make([]byte, 512)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
switch strings.TrimSpace(scanner.Text()) {
case "STREAM" :
continue
case "STATUS" :
continue
case "RESULT=OK" :
port,_ := strconv.Atoi(lport)
return &StreamListener{conn, listener, port, s.keys.Addr()}, nil
case "RESULT=I2P_ERROR" :
conn.Close()
return nil, errors.New("I2P internal error")
case "RESULT=INVALID_ID" :
conn.Close()
return nil, errors.New("Invalid tunnel ID")
default :
conn.Close()
return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
}
}
panic("sam3 go library error in StreamSession.Listener()")
}
// Implements net.Listener for I2P streaming sessions
type StreamListener struct {
conn net.Conn
listener net.Listener
lport int
laddr I2PAddr
}
const defaultListenReadLen = 516
// Accepts incomming connections to your StreamSession tunnel. Implements net.Listener
func (l *StreamListener) Accept() (*SAMConn, error) {
conn, err := l.listener.Accept()
if err != nil {
return nil, err
}
buf := make([]byte, defaultListenReadLen)
n, err := conn.Read(buf)
if n < defaultListenReadLen {
return nil, errors.New("Unknown destination type: " + string(buf[:n]))
}
// I2P inserts the I2P address ("destination") of the connecting peer into the datastream, followed by
// a \n. Since the length of a destination may vary, this reads until a newline is found. At the time
// of writing, the length is never less then, and almost always equals 516 bytes, which is why
// defaultListenReadLen is 516.
if rune(buf[defaultListenReadLen - 1]) != '\n' {
abuf := make([]byte, 1)
for {
n, err := conn.Read(abuf)
if n != 1 || err != nil {
return nil, errors.New("Failed to decode connecting peers I2P destination.")
}
buf = append(buf, abuf[0])
if rune(abuf[0]) == '\n' { break }
}
}
rAddr, err := NewI2PAddrFromString(string(buf[:len(buf)-1])) // the address minus the trailing newline
if err != nil {
conn.Close()
return nil, errors.New("Could not determine connecting tunnels address.")
}
return &SAMConn{l.laddr, rAddr, conn}, nil
}
// Implements net.Listener
func (l *StreamListener) Close() error {
err := l.listener.Close()
err2 := l.conn.Close()
if err2 != nil {
return err2
}
return err
}
// Implements net.Listener
func (l *StreamListener) Addr() net.Addr {
return l.laddr
}

36
suggestedOptions.go Normal file
View File

@ -0,0 +1,36 @@
package sam3
var (
// Suitable options if you are shuffling A LOT of traffic. If unused, this
// will waste your resources.
Options_Humongous = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=3", "outbound.backupQuantity=3",
"inbound.quantity=6", "outbound.quantity=6"}
// Suitable for shuffling a lot of traffic.
Options_Fat = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=1", "outbound.backupQuantity=1",
"inbound.quantity=4", "outbound.quantity=4"}
// Suitable for shuffling medium amounts of traffic.
Options_Medium = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2"}
// Suitable for small and quick dataflows.
Options_Small = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=1", "outbound.quantity=1"}
// Does not use any anonymization, you connect directly to others tunnel
// endpoints, thus revealing your identity but not theirs. Use this only
// if you don't care.
Options_Warning_ZeroHop = []string{"inbound.length=0", "outbound.length=0",
"inbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2"}
)