sam3

package
v0.0.0-...-afc8e46 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2017 License: MIT Imports: 13 Imported by: 0

README

README

go library for the I2P SAMv3.0 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 domain names to I2P destinations
    • .b32.i2p hashes
    • Generating keys/i2p destinations
  • Streaming
    • DialI2P() - Connecting to stuff in I2P
    • Listen()/Accept() - Handling incomming connections
    • Implements net.Conn and net.Listener
  • Datagrams
    • Implements net.PacketConn
  • Raw datagrams
    • Like datagrams, but without addresses

Does not work:

  • Everything works! :D
  • Probably needs some real-world testing

Documentation

  • Latest version-documentation:
    • set your GOPATH
    • Enter godoc -http=:8081 into your terminal and hit enter.
    • Goto http://localhost:8081, click packages, and navigate to sam3

Examples

package main

import (
	"github.com/majestrate/i2p-tools/sam3"
	"fmt"
)

const yoursam = "127.0.0.1:7656" // sam bridge

func client(server I2PAddr) {
	sam, _ := NewSAM(yoursam)
	keys, _ := sam.NewKeys()
	stream, _ := sam.NewStreamSession("clientTun", keys, Options_Small)
	fmt.Println("Client: Connecting to " + server.Base32())
	conn, _ := stream.DialI2P(server)
	conn.Write([]byte("Hello world!"))
	return
}

func main() {
	sam, _ := NewSAM(yoursam)
	keys, _ := sam.NewKeys()
	go client(keys.Addr())
	stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
	listener, _ := stream.Listen()
	conn, _ := listener.Accept()
	buf := make([]byte, 4096)
	n, _ := conn.Read(buf)
	fmt.Println("Server received: " + string(buf[:n]))
}

The above will write to the terminal:

Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
Server received: Hello world!

Error handling was omitted in the above code for readability.

Testing

  • go test runs the whole suite (takes 90+ sec to perform!)
  • go test -short runs the shorter variant, does not connect to anything

License

Public domain.

Author

Kalle Vedin kalle.vedin@fripost.org

Documentation

Overview

Library for I2Ps SAMv3 bridge (https://geti2p.com)

Index

Examples

Constants

This section is empty.

Variables

View Source
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 only for small dataflows, and very short lasting connections:
	// You only have one tunnel in each direction, so if any of the nodes
	// through which any of your two tunnels pass through go offline, there will
	// be a complete halt in the dataflow, until a new tunnel is built.
	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"}
)

Examples and suggestions for options when creating sessions.

Functions

func Base32

func Base32(anything string) string

Makes any string into a *.b32.i2p human-readable I2P address. This makes no sense, unless "anything" is an I2P destination of some sort.

func StoreKeysIncompat

func StoreKeysIncompat(k I2PKeys, w io.Writer) (err error)

store keys in non standard format

Types

type Config

type Config struct {
	Addr    string
	Opts    Options
	Session string
	Keyfile string
}

Config is the config type for the sam connector api for i2p which allows applications to 'speak' with i2p

func (*Config) DatagramSession

func (cfg *Config) DatagramSession() (session *DatagramSession, err error)

create new sam datagram session from config

func (*Config) StreamSession

func (cfg *Config) StreamSession() (session *StreamSession, err error)

create new sam connector from config with a stream session

type DatagramSession

type DatagramSession struct {
	// contains filtered or unexported fields
}

The DatagramSession implements net.PacketConn. It works almost like ordinary UDP, except that datagrams may be at most 31kB large. These datagrams are also end-to-end encrypted, signed and includes replay-protection. And they are also built to be surveillance-resistant (yey!).

Example
// Creates a new DatagramSession, which behaves just like a net.PacketConn.

const samBridge = "127.0.0.1:7656"

sam, err := NewSAM(samBridge)
if err != nil {
	fmt.Println(err.Error())
	return
}
keys, err := sam.NewKeys()
if err != nil {
	fmt.Println(err.Error())
	return
}
myself := keys.Addr()

// See the example Option_* variables.
dg, err := sam.NewDatagramSession("DGTUN", keys, Options_Small, 0)
if err != nil {
	fmt.Println(err.Error())
	return
}
someone, err := sam.Lookup("zzz.i2p")
if err != nil {
	fmt.Println(err.Error())
	return
}

dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)

buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println("Got message: " + string(buf[:n]))

return
Output:

Got message: Hello myself!

func (*DatagramSession) B32

func (s *DatagramSession) B32() string

func (*DatagramSession) Close

func (s *DatagramSession) Close() error

Closes the DatagramSession. Implements net.PacketConn

func (*DatagramSession) LocalAddr

func (s *DatagramSession) LocalAddr() net.Addr

Implements net.PacketConn

func (*DatagramSession) LocalI2PAddr

func (s *DatagramSession) LocalI2PAddr() I2PAddr

Returns the I2P destination of the DatagramSession.

func (*DatagramSession) Lookup

func (s *DatagramSession) Lookup(name string) (a net.Addr, err error)

func (*DatagramSession) ReadFrom

func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error)

Reads one datagram sent to the destination of the DatagramSession. Returns the number of bytes read, from what address it was sent, or an error. implements net.PacketConn

func (*DatagramSession) SetDeadline

func (s *DatagramSession) SetDeadline(t time.Time) error

Sets read and write deadlines for the DatagramSession. Implements net.PacketConn and does the same thing. Setting write deadlines for datagrams is seldom done.

func (*DatagramSession) SetReadDeadline

func (s *DatagramSession) SetReadDeadline(t time.Time) error

Sets read deadline for the DatagramSession. Implements net.PacketConn

func (*DatagramSession) SetWriteDeadline

func (s *DatagramSession) SetWriteDeadline(t time.Time) error

Sets the write deadline for the DatagramSession. Implements net.Packetconn.

func (*DatagramSession) WriteTo

func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error)

Sends one signed datagram to the destination specified. At the time of writing, maximum size is 31 kilobyte, but this may change in the future. Implements net.PacketConn.

type I2PAddr

type I2PAddr string

I2PAddr represents an I2P destination, almost equivalent to an IP address. This is the humongously huge base64 representation of such an address, which really is just a pair of public keys and also maybe a certificate. (I2P hides the details of exactly what it is. Read the I2P specifications for more info.)

func NewI2PAddrFromBytes

func NewI2PAddrFromBytes(addr []byte) (I2PAddr, error)

Creates a new I2P address from a byte array. The inverse of ToBytes().

func NewI2PAddrFromString

func NewI2PAddrFromString(addr string) (I2PAddr, error)

Creates a new I2P address from a base64-encoded string. Checks if the address addr is in correct format. (If you know for sure it is, use I2PAddr(addr).)

func (I2PAddr) Base32

func (addr I2PAddr) Base32() (str string)

Returns the *.b32.i2p address of the I2P address. It is supposed to be a somewhat human-manageable 64 character long pseudo-domain name equivalent of the 516+ characters long default base64-address (the I2PAddr format). It is not possible to turn the base32-address back into a usable I2PAddr without performing a Lookup(). Lookup only works if you are using the I2PAddr from which the b32 address was generated.

func (I2PAddr) Base64

func (a I2PAddr) Base64() string

Returns the base64 representation of the I2PAddr

func (I2PAddr) DestHash

func (addr I2PAddr) DestHash() (h I2PDestHash)

func (I2PAddr) Network

func (a I2PAddr) Network() string

Returns "I2P"

func (I2PAddr) String

func (a I2PAddr) String() string

Returns the I2P destination (base64-encoded)

func (I2PAddr) ToBytes

func (addr I2PAddr) ToBytes() ([]byte, error)

Turns an I2P address to a byte array. The inverse of NewI2PAddrFromBytes().

type I2PDestHash

type I2PDestHash [32]byte

an i2p destination hash, the .b32.i2p address if you will

func DestHashFromString

func DestHashFromString(str string) (dhash I2PDestHash, err error)

create a desthash from a string b32.i2p address

func (I2PDestHash) String

func (h I2PDestHash) String() string

get string representation of i2p dest hash

type I2PKeys

type I2PKeys struct {
	// contains filtered or unexported fields
}

The public and private keys associated with an I2P destination. I2P hides the details of exactly what this is, so treat them as blobs, but generally: One pair of DSA keys, one pair of ElGamal keys, and sometimes (almost never) also a certificate. String() returns you the full content of I2PKeys and Addr() returns the public keys.

func LoadKeysIncompat

func LoadKeysIncompat(r io.Reader) (k I2PKeys, err error)

load keys from non standard format

func NewKeys

func NewKeys(addr I2PAddr, both string) I2PKeys

Creates I2PKeys from an I2PAddr and a public/private keypair string (as generated by String().)

func (I2PKeys) Addr

func (k I2PKeys) Addr() I2PAddr

Returns the public keys of the I2PKeys.

func (I2PKeys) String

func (k I2PKeys) String() string

Returns the keys (both public and private), in I2Ps base64 format. Use this when you create sessions.

type Options

type Options map[string]string

options map

func (Options) AsList

func (opts Options) AsList() (ls []string)

obtain sam options as list of strings

type RawSession

type RawSession struct {
	// contains filtered or unexported fields
}

The RawSession provides no authentication of senders, and there is no sender address attached to datagrams, so all communication is anonymous. The messages send are however still endpoint-to-endpoint encrypted. You need to figure out a way to identify and authenticate clients yourself, iff that is needed. Raw datagrams may be at most 32 kB in size. There is no overhead of authentication, which is the reason to use this..

func (*RawSession) Close

func (s *RawSession) Close() error

Closes the RawSession.

func (*RawSession) LocalAddr

func (s *RawSession) LocalAddr() I2PAddr

Returns the local I2P destination of the RawSession.

func (*RawSession) Read

func (s *RawSession) Read(b []byte) (n int, err error)

Reads one raw datagram sent to the destination of the DatagramSession. Returns the number of bytes read. Who sent the raw message can not be determined at this layer - you need to do it (in a secure way!).

func (*RawSession) SetDeadline

func (s *RawSession) SetDeadline(t time.Time) error

func (*RawSession) SetReadDeadline

func (s *RawSession) SetReadDeadline(t time.Time) error

func (*RawSession) SetWriteDeadline

func (s *RawSession) SetWriteDeadline(t time.Time) error

func (*RawSession) WriteTo

func (s *RawSession) WriteTo(b []byte, addr I2PAddr) (n int, err error)

Sends one raw datagram to the destination specified. At the time of writing, maximum size is 32 kilobyte, but this may change in the future.

type SAM

type SAM struct {
	// contains filtered or unexported fields
}

Used for controlling I2Ps SAMv3.

func NewSAM

func NewSAM(address string) (*SAM, error)

Creates a new controller for the I2P routers SAM bridge.

func (*SAM) Close

func (sam *SAM) Close() error

close this sam session

func (*SAM) EnsureKeyfile

func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error)

if keyfile fname does not exist

func (*SAM) Keys

func (sam *SAM) Keys() (k *I2PKeys)

func (*SAM) Lookup

func (sam *SAM) Lookup(name string) (I2PAddr, error)

Performs a lookup, probably this order: 1) routers known addresses, cached addresses, 3) by asking peers in the I2P network.

func (*SAM) NewDatagramSession

func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpPort int) (*DatagramSession, error)

Creates a new datagram session. udpPort is the UDP port SAM is listening on, and if you set it to zero, it will use SAMs standard UDP port.

func (*SAM) NewKeys

func (sam *SAM) NewKeys() (I2PKeys, error)

Creates the I2P-equivalent of an IP address, that is unique and only the one who has the private keys can send messages from. The public keys are the I2P desination (the address) that anyone can send messages to.

func (*SAM) NewRawSession

func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort int) (*RawSession, error)

Creates a new raw session. udpPort is the UDP port SAM is listening on, and if you set it to zero, it will use SAMs standard UDP port.

func (*SAM) NewStreamSession

func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error)

Creates a new StreamSession with the I2CP- and streaminglib options as specified. See the I2P documentation for a full list of options.

func (*SAM) ReadKeys

func (sam *SAM) ReadKeys(r io.Reader) (err error)

read public/private keys from an io.Reader

type SAMConn

type SAMConn struct {
	// contains filtered or unexported fields
}

Implements net.Conn

func (*SAMConn) Close

func (sc *SAMConn) Close() error

Implements net.Conn

func (*SAMConn) LocalAddr

func (sc *SAMConn) LocalAddr() net.Addr

func (*SAMConn) Read

func (sc *SAMConn) Read(buf []byte) (int, error)

Implements net.Conn

func (*SAMConn) RemoteAddr

func (sc *SAMConn) RemoteAddr() net.Addr

func (*SAMConn) SetDeadline

func (sc *SAMConn) SetDeadline(t time.Time) error

Implements net.Conn

func (*SAMConn) SetReadDeadline

func (sc *SAMConn) SetReadDeadline(t time.Time) error

Implements net.Conn

func (*SAMConn) SetWriteDeadline

func (sc *SAMConn) SetWriteDeadline(t time.Time) error

Implements net.Conn

func (*SAMConn) Write

func (sc *SAMConn) Write(buf []byte) (int, error)

Implements net.Conn

type StreamListener

type StreamListener struct {
	// contains filtered or unexported fields
}
Example
// One server Accept()ing on a StreamListener, and one client that Dials
// through I2P to the server. Server writes "Hello world!" through a SAMConn
// (which implements net.Conn) and the client prints the message.

const samBridge = "127.0.0.1:7656"

sam, err := NewSAM(samBridge)
if err != nil {
	fmt.Println(err.Error())
	return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
	fmt.Println(err.Error())
	return
}

quit := make(chan bool)

// Client connecting to the server
go func(server I2PAddr) {
	csam, err := NewSAM(samBridge)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer csam.Close()
	keys, err := csam.NewKeys()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	cs, err := csam.NewStreamSession("client_example", keys, Options_Small)
	if err != nil {
		fmt.Println(err.Error())
		quit <- false
		return
	}
	conn, err := cs.DialI2P(server)
	if err != nil {
		fmt.Println(err.Error())
		quit <- false
		return
	}
	buf := make([]byte, 256)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println(err.Error())
		quit <- false
		return
	}
	fmt.Println(string(buf[:n]))
	quit <- true
}(keys.Addr()) // end of client

ss, err := sam.NewStreamSession("server_example", keys, Options_Small)
if err != nil {
	fmt.Println(err.Error())
	return
}
l, err := ss.Listen()
if err != nil {
	fmt.Println(err.Error())
	return
}
conn, err := l.Accept()
if err != nil {
	fmt.Println(err.Error())
	return
}
conn.Write([]byte("Hello world!"))

<-quit // waits for client to die, for example only
Output:

Hello world!

func (*StreamListener) Accept

func (l *StreamListener) Accept() (net.Conn, error)

implements net.Listener

func (*StreamListener) AcceptI2P

func (l *StreamListener) AcceptI2P() (*SAMConn, error)

accept a new inbound connection

func (*StreamListener) Addr

func (l *StreamListener) Addr() net.Addr

get our address implements net.Listener

func (*StreamListener) Close

func (l *StreamListener) Close() error

implements net.Listener

type StreamSession

type StreamSession struct {
	// contains filtered or unexported fields
}

Represents a streaming session.

Example
// Creates a new StreamingSession, dials to zzz.i2p and gets a SAMConn
// which behaves just like a normal net.Conn.

const samBridge = "127.0.0.1:7656"

sam, err := NewSAM(samBridge)
if err != nil {
	fmt.Println(err.Error())
	return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
	fmt.Println(err.Error())
	return
}
// See the example Option_* variables.
ss, err := sam.NewStreamSession("stream_example", keys, Options_Small)
if err != nil {
	fmt.Println(err.Error())
	return
}
someone, err := sam.Lookup("zzz.i2p")
if err != nil {
	fmt.Println(err.Error())
	return
}

conn, err := ss.DialI2P(someone)
if err != nil {
	fmt.Println(err.Error())
	return
}
defer conn.Close()
fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
	fmt.Println(err.Error())
	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("Probably failed to StreamSession.DialI2P(zzz.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
	fmt.Println("Read HTTP/HTML from zzz.i2p")
}
return
Output:

Sending HTTP GET /
Read HTTP/HTML from zzz.i2p

func (StreamSession) Addr

func (ss StreamSession) Addr() I2PAddr

Returns the I2P destination (the address) of the stream session

func (*StreamSession) Close

func (ss *StreamSession) Close() error

func (*StreamSession) Dial

func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error)

implement net.Dialer

func (*StreamSession) DialI2P

func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error)

Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.

func (StreamSession) ID

func (ss StreamSession) ID() string

Returns the local tunnel name of the I2P tunnel used for the stream session

func (StreamSession) Keys

func (ss StreamSession) Keys() I2PKeys

Returns the keys associated with the stream session

func (*StreamSession) Listen

func (s *StreamSession) Listen() (*StreamListener, error)

create a new stream listener to accept inbound connections

func (*StreamSession) Lookup

func (s *StreamSession) Lookup(name string) (I2PAddr, error)

lookup name, convienence function

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL