72 Commits

Author SHA1 Message Date
idk
eabe42f33f completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:22 -04:00
idk
0e9665027e completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:16 -04:00
idk
79b526d69c test, fmt, update changelog and version number 2019-05-18 18:34:57 -04:00
idk
5b1dcc54aa Create stable branch 2019-05-18 18:32:21 -04:00
idk
6b863d7514 Create stable branch 2019-05-18 18:32:12 -04:00
idk
9ee74a89d3 Create stable branch 2019-05-18 18:31:44 -04:00
idk
6029d7dd6e Create stable branch 2019-05-18 18:30:22 -04:00
idk
d09fa462ed export sig constants 2019-03-26 23:19:10 -04:00
idk
4a6b7966f2 export sig constants 2019-03-26 23:17:57 -04:00
idk
d1d27c55aa add FROM_PORT and TO_PORT options to streaming 2019-03-26 23:09:53 -04:00
idk
d422604759 add FROM_PORT and TO_PORT options to streaming 2019-03-26 23:04:25 -04:00
idk
1e3684b26e Better setup for signatures 2019-03-26 22:22:00 -04:00
idk
917c6a7c49 only run offline tests 2019-02-28 21:45:25 -05:00
idk
a42f47931b update package 2019-02-28 21:40:30 -05:00
idk
8ed385c394 switch it to being debian-native 2019-02-28 21:28:49 -05:00
idk
3ae3281168 fix changelog 2019-02-25 11:14:20 -05:00
idk
ee0fb2c871 re-add in debian folder, may require editing 2019-02-25 11:13:37 -05:00
idk
a6914d7d9b re-add in debian folder, may require editing 2019-02-25 11:13:03 -05:00
idk
af5a3f3239 add unlicense 2019-02-23 13:50:58 -05:00
idk
2e872c6b20 Add signature support 2019-02-13 23:58:24 -05:00
idk
337bae0956 revert back to pointers 2019-02-09 17:08:20 -05:00
idk
ab5663c157 revert back to pointers 2019-02-09 17:01:29 -05:00
idk
554202709f update the DestHash structure 2018-12-25 21:20:46 -05:00
idk
d1a21d339f always something new to know 2018-12-25 04:21:03 -05:00
idk
07ebb0f823 always something new to know 2018-12-25 04:19:40 -05:00
idk
21b597c509 Make a standalone resolver 2018-12-24 23:50:26 -05:00
idk
93ee7a66e9 Start creating a standalone 'resolver' 2018-12-18 16:59:13 -05:00
idk
b81d625bea Actually add some context-awareness to DialContext* 2018-12-18 14:38:53 -05:00
idk
313d32ccf3 gofmt 2018-11-27 21:56:58 -05:00
idk
4065aba3ee context-aware dialer, well faking it 2018-11-27 21:56:34 -05:00
idk
5df47060c9 context-aware dialer, well faking it 2018-11-27 21:46:02 -05:00
idk
aebf5a4245 context-aware dialer 2018-11-27 21:36:33 -05:00
idk
acc809d485 I... don't think any of these should have pointer recievers, right? Switching them hasn't broken anything yet, that's why this is a fork, YOLO. 2018-11-27 12:25:30 -05:00
idk
0152ed864d I think I have some more work to do in here... 2018-11-27 12:10:38 -05:00
idk
c77a5d980c fixed typo 2018-09-26 11:29:22 -04:00
idk
d8ee0fca12 added an alias for ToBytes as Bytes because I need it to implement MultiAddr. 2018-09-26 11:27:12 -04:00
idk
4babdcd9f7 reverse that change. 2018-09-23 02:05:17 -04:00
idk
4949b11b57 updates 2018-09-19 14:35:28 -04:00
idk
c1321ac144 updates 2018-09-19 14:29:47 -04:00
idk
f7d729017d fix some tests 2018-09-09 01:34:48 -04:00
idk
f3a993315a Change to implement updated net.Conn 2018-09-09 01:33:24 -04:00
16e9417d2e merging from i2ptools/master subfolder 2016-09-12 01:11:35 -04:00
d0e367fbab fix sam3 2016-05-09 06:52:54 -04:00
090712478f in sam3 stream try many times to accept inbound connection 2016-05-07 08:51:59 -04:00
5233589f4a add i2p.PacketSession that implements net.PacketConn 2016-02-17 08:47:01 -05:00
700e096281 addr -> host 2016-02-10 19:16:00 -05:00
7edd91a383 go fmt + try fixing leak 2016-02-10 18:23:41 -05:00
fcc4565138 try fixing leaking sockets 2016-02-10 18:11:05 -05:00
60182b22f8 try fixing leaking connections 2016-02-10 17:58:03 -05:00
59f815f2e7 go fmt 2016-02-10 17:54:17 -05:00
e6936eed2f api fixups 2016-02-10 17:36:15 -05:00
99b4ecf533 don't leak 2016-02-05 16:34:03 -05:00
1d0d324d13 fix up sam3 2016-01-31 10:38:14 -05:00
9bbc5249e1 add go bindings for i2p that implement net.Listener etc 2016-01-15 08:49:14 -05:00
5a0f4e39dc add i2maild initial commit 2015-12-24 12:59:57 -05:00
3247769471 only dial out when name resolution succeedes 2015-12-22 22:48:10 -05:00
87e20ae658 fixups in sam3 2015-12-22 12:10:29 -05:00
0143d1df57 have stream listener close parent session on close 2015-12-17 11:05:16 -05:00
b8a2f40bf1 don't close nil sam session 2015-12-17 11:02:19 -05:00
b51a8977cf update sam3 libs to try fixing socket leaking in libi2ptorrent seeder 2015-12-17 11:01:04 -05:00
298b55c427 add streamsession.close 2015-12-14 17:13:45 -05:00
92493aa92a fix more leaking connections 2015-12-14 17:08:14 -05:00
6f95da72ea close leaking connections in stream 2015-12-14 17:01:06 -05:00
c6eba7d0a0 add transient keys and dialer 2015-12-14 13:58:45 -05:00
b500865ffe add config stubs 2015-12-14 09:00:11 -05:00
20095ae901 use sam STREAM ACCEPT instead of STREAM FORWARD for streams 2015-12-13 11:47:25 -05:00
13894c0a14 fix sam stream options, it's not OPTION=i2cp.option=value it's i2cp.option=value 2015-12-07 17:37:52 -05:00
d32071ba2e create keyfile 2015-11-30 12:48:45 -05:00
a30f0eded7 have sam3 streams actually implement the interfaces they say they do 2015-11-30 12:38:18 -05:00
937d862d90 have ensurekeyfile return the i2p keys 2015-11-30 11:36:49 -05:00
a1d2ac25d6 update sam3 libs to have more 2015-11-30 11:35:25 -05:00
4108184369 bundle sam3 2015-10-15 17:21:11 -04:00
25 changed files with 972 additions and 385 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
*~
*.swp
README.md.asc

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>

View File

@ -28,8 +28,6 @@ This library is much better than ccondom (that use BOB), much more stable and mu
## Documentation ##
* [Online cached version](http://godoc.org/bitbucket.org/kallevedin/sam3)
* Latest version-documentation:
* set your GOPATH
* Enter `godoc -http=:8081` into your terminal and hit enter.
@ -40,7 +38,7 @@ This library is much better than ccondom (that use BOB), much more stable and mu
package main
import (
"bitbucket.org/kallevedin/sam3"
"github.com/majestrate/i2p-tools/sam3"
"fmt"
)
@ -80,14 +78,14 @@ Error handling was omitted in the above code for readability.
## Testing ##
* `go test` runs the whole suite (takes 90+ sec to perform!)
* `go test -tags=nettest` runs the whole suite (takes 90+ sec to perform!)
* `go test -short` runs the shorter variant, does not connect to anything
## License ##
Public domain.
## Authors ##
## Author ##
* Kalle Vedin `kalle.vedin@fripost.org`
* Unknown Name (majestrate)
* Unknown Name (majestrate)

105
README.md.asc Normal file
View File

@ -0,0 +1,105 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
# README #
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/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 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 ##
```go
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:
```text
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`
* Unknown Name (majestrate)
-----BEGIN PGP SIGNATURE-----
iQEzBAEBCgAdFiEEcNIGBzi++AUjrK/311wDs5teFOEFAlxk9W0ACgkQ11wDs5te
FOEl8wf/VinlOV3Op5fZ6Qr9eTanj85FoH9KbjA0P6EcUJap4QWoEUIK/mkIlhl/
jLdAMHry5K3gXObFrVAiO4XvsAF++/JJQroUWPb++UR9ksd+M4b63ia3/BtFQGUu
J2w6dK7S79Z6IumhA6xhdDaIRGiJSu8Ox0M36ZKkfAR3WTYfsz+Nucp+1l8otNhI
fjQrmLCSgAiaCW0h3m208JR20FYipjHc7CZBzF/TVCjPq9qleEMXQCTJBWcuibrR
RdoUct6oMAXNS4S7k4LYa5FK0ETNIap9aUyFa8Tp6tmzhSOun7/Gle3ynH/c9SrO
lopjX4BT4mpXN1Lvwdw+v3tTZPyuWQ==
=lYsZ
-----END PGP SIGNATURE-----

View File

@ -1,57 +1,64 @@
package sam3
import (
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"net"
"time"
)
// Implements net.Conn
type SAMConn struct {
laddr I2PAddr
raddr I2PAddr
conn net.Conn
laddr i2pkeys.I2PAddr
raddr i2pkeys.I2PAddr
conn net.Conn
}
// Implements net.Conn
func (sc SAMConn) Read(buf []byte) (int, error) {
func (sc *SAMConn) Read(buf []byte) (int, error) {
n, err := sc.conn.Read(buf)
return n, err
}
// Implements net.Conn
func (sc SAMConn) Write(buf []byte) (int, error) {
func (sc *SAMConn) Write(buf []byte) (int, error) {
n, err := sc.conn.Write(buf)
return n, err
}
// Implements net.Conn
func (sc SAMConn) Close() error {
func (sc *SAMConn) Close() error {
return sc.conn.Close()
}
// Implements net.Conn
func (sc SAMConn) LocalAddr() I2PAddr {
return sc.laddr
func (sc *SAMConn) LocalAddr() net.Addr {
return sc.localAddr()
}
// Implements net.Conn
func (sc SAMConn) RemoteAddr() I2PAddr {
func (sc *SAMConn) localAddr() i2pkeys.I2PAddr {
return sc.laddr
}
func (sc *SAMConn) RemoteAddr() net.Addr {
return sc.remoteAddr()
}
// Implements net.Conn
func (sc *SAMConn) remoteAddr() i2pkeys.I2PAddr {
return sc.raddr
}
// Implements net.Conn
func (sc SAMConn) SetDeadline(t time.Time) error {
func (sc *SAMConn) SetDeadline(t time.Time) error {
return sc.conn.SetDeadline(t)
}
// Implements net.Conn
func (sc SAMConn) SetReadDeadline(t time.Time) error {
func (sc *SAMConn) SetReadDeadline(t time.Time) error {
return sc.conn.SetReadDeadline(t)
}
// Implements net.Conn
func (sc SAMConn) SetWriteDeadline(t time.Time) error {
func (sc *SAMConn) SetWriteDeadline(t time.Time) error {
return sc.conn.SetWriteDeadline(t)
}

75
config.go Normal file
View File

@ -0,0 +1,75 @@
package sam3
import (
"fmt"
"net"
"strconv"
"github.com/eyedeekay/sam3/i2pkeys"
)
// sam config
// options map
type Options map[string]string
// obtain sam options as list of strings
func (opts Options) AsList() (ls []string) {
for k, v := range opts {
ls = append(ls, fmt.Sprintf("%s=%s", k, v))
}
return
}
// Config is the config type for the sam connector api for i2p which allows applications to 'speak' with i2p
type Config struct {
Addr string
Opts Options
Session string
Keyfile string
}
// create new sam connector from config with a stream session
func (cfg *Config) StreamSession() (session *StreamSession, err error) {
// connect
var s *SAM
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// create session
session, err = s.NewStreamSession(cfg.Session, keys, cfg.Opts.AsList())
}
}
return
}
// create new sam datagram session from config
func (cfg *Config) DatagramSession() (session *DatagramSession, err error) {
// connect
var s *SAM
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// determine udp port
var portstr string
_, portstr, err = net.SplitHostPort(cfg.Addr)
if err == nil {
var port int
port, err = strconv.Atoi(portstr)
if err == nil && port > 0 {
// udp port is 1 lower
port--
// create session
session, err = s.NewDatagramSession(cfg.Session, keys, cfg.Opts.AsList(), port)
}
}
}
}
return
}

View File

@ -6,24 +6,26 @@ import (
"net"
"strconv"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
)
// 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
// also end-to-end encrypted, signed and includes replay-protection. And they
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys i2pkeys.I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
// 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 (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
@ -35,7 +37,7 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost + ":0")
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
return nil, err
}
@ -48,7 +50,7 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost + ":" + strconv.Itoa(udpPort))
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
return nil, err
}
@ -60,18 +62,23 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr}, nil
}
// Reads one datagram sent to the destination of the DatagramSession. Returns
func (s *DatagramSession) B32() string {
return s.keys.Addr().Base32()
}
// 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.
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr I2PAddr, err error) {
// implements net.PacketConn
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
// extra bytes to read the remote address of incomming datagram
buf := make([]byte, len(b) + 4096)
buf := make([]byte, len(b)+4096)
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.udpconn.ReadFromUDP(buf)
if err != nil {
return 0, I2PAddr(""), err
return 0, i2pkeys.I2PAddr(""), err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
continue
@ -80,26 +87,26 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr I2PAddr, err error) {
}
i := bytes.IndexByte(buf, byte('\n'))
if i > 4096 || i > n {
return 0, I2PAddr(""), errors.New("Could not parse incomming message remote address.")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address.")
}
raddr, err := NewI2PAddrFromString(string(buf[:i]))
raddr, err := i2pkeys.NewI2PAddrFromString(string(buf[:i]))
if err != nil {
return 0, I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
}
// shift out the incomming address to contain only the data received
if ( n - i+1 ) > len(b) {
if (n - i + 1) > len(b) {
copy(b, buf[i+1:i+1+len(b)])
return n-(i+1), raddr, errors.New("Datagram did not fit into your buffer.")
return n - (i + 1), raddr, errors.New("Datagram did not fit into your buffer.")
} else {
copy(b, buf[i+1:n])
return n-(i+1), raddr, nil
return n - (i + 1), raddr, nil
}
}
// Sends one signed datagram to the destination specified. At the time of
// 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.
func (s *DatagramSession) WriteTo(b []byte, addr I2PAddr) (n int, err error) {
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
@ -116,12 +123,27 @@ func (s *DatagramSession) Close() error {
return err2
}
// Returns the I2P destination of the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) LocalAddr() I2PAddr {
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
}
// Sets read and write deadlines for the DatagramSession. Implements
// Implements net.PacketConn
func (s *DatagramSession) LocalAddr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
var sam *SAM
sam, err = NewSAM(s.samAddr)
if err == nil {
defer sam.Close()
a, err = sam.Lookup(name)
}
return
}
// 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 (s *DatagramSession) SetDeadline(t time.Time) error {
@ -137,5 +159,3 @@ func (s *DatagramSession) SetReadDeadline(t time.Time) error {
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
return s.udpconn.SetWriteDeadline(t)
}

View File

@ -1,3 +1,5 @@
// +build nettest
package sam3
import (
@ -6,7 +8,6 @@ import (
"time"
)
func Test_DatagramServerClient(t *testing.T) {
if testing.Short() {
return
@ -24,7 +25,7 @@ func Test_DatagramServerClient(t *testing.T) {
t.Fail()
return
}
// fmt.Println("\tServer: My address: " + keys.Addr().Base32())
// fmt.Println("\tServer: My address: " + keys.Addr().Base32())
fmt.Println("\tServer: Creating tunnel")
ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
@ -33,7 +34,7 @@ func Test_DatagramServerClient(t *testing.T) {
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan(bool)) {
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
@ -52,22 +53,22 @@ func Test_DatagramServerClient(t *testing.T) {
return
}
defer ds2.Close()
// fmt.Println("\tClient: Servers address: " + ds.LocalAddr().Base32())
// fmt.Println("\tClient: Clients address: " + ds2.LocalAddr().Base32())
// fmt.Println("\tClient: Servers address: " + ds.LocalAddr().Base32())
// fmt.Println("\tClient: Clients address: " + ds2.LocalAddr().Base32())
fmt.Println("\tClient: Tries to send datagram to server")
for {
select {
default :
_, err = ds2.WriteTo([]byte("Hello datagram-world! <3 <3 <3 <3 <3 <3"), ds.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w :
fmt.Println("\tClient: Sent datagram, quitting.")
default:
_, err = ds2.WriteTo([]byte("Hello datagram-world! <3 <3 <3 <3 <3 <3"), ds.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent datagram, quitting.")
return
}
}
c <- true
@ -82,19 +83,14 @@ func Test_DatagramServerClient(t *testing.T) {
return
}
fmt.Println("\tServer: Received datagram: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}
func ExampleDatagramSession() {
// 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())
@ -134,4 +130,3 @@ func ExampleDatagramSession() {
// Output:
//Got message: Hello myself!
}

15
debian/changelog vendored Normal file
View File

@ -0,0 +1,15 @@
golang-github-eyedeekay-sam3 (0.3.2.0) bionic; urgency=medium
[ idk ]
* Bug fixes, create stable branch
* Move i2pkeys
-- idk <hankhill19580@gmail.com> Fri, 18 May 2019 18:32:51 -0500
golang-github-eyedeekay-sam3 (0.0~git20190223.af5a3f3) bionic; urgency=medium
[ idk ]
* Initial release (Closes: TODO)
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 21:40:35 -0500

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
11

24
debian/control vendored Normal file
View File

@ -0,0 +1,24 @@
Source: golang-github-eyedeekay-sam3
Section: devel
Priority: optional
Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>
Uploaders: idk <hankhill19580@gmail.com>
Build-Depends: debhelper (>= 11),
dh-golang,
golang-any
Standards-Version: 4.2.1
Homepage: https://github.com/eyedeekay/sam3
Vcs-Browser: https://github.com/eyedeekay/sam3
Vcs-Git: https://github.com/eyedeekay/sam3.git
XS-Go-Import-Path: github.com/eyedeekay/sam3
Testsuite: autopkgtest-pkg-go
Package: golang-github-eyedeekay-sam3-dev
Architecture: all
Depends: ${misc:Depends},
i2p | i2pd
Description: Go library for the I2P SAMv3.0 bridge,
used to build anonymous/pseudonymous end-to-end encrypted sockets.
README go library for the I2P SAMv3.0
(https://geti2p.net/en/docs/api/samv3) bridge, used to build
anonymous/pseudonymous end-to-end encrypted sockets.

17
debian/copyright vendored Normal file
View File

@ -0,0 +1,17 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: sam3
Source: https://github.com/eyedeekay/sam3
Files-Excluded:
Godeps/_workspace
Files: *
Copyright: 2016 idk
License: TODO
Files: debian/*
Copyright: 2019 idk <hankhill19580@gmail.com>
License: TODO
Comment: Debian packaging is licensed under the same terms as upstream
License: TODO
TODO

1
debian/files vendored Normal file
View File

@ -0,0 +1 @@
golang-github-eyedeekay-sam3_0.3.2.0_source.buildinfo devel optional

2
debian/gbp.conf vendored Normal file
View File

@ -0,0 +1,2 @@
[DEFAULT]
pristine-tar = True

4
debian/rules vendored Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/make -f
%:
dh $@ --buildsystem=golang --with=golang

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

4
debian/watch vendored Normal file
View File

@ -0,0 +1,4 @@
version=4
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/golang-github-eyedeekay-sam3-\$1\.tar\.gz/,\
uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \
https://github.com/eyedeekay/sam3/tags .*/v?(\d\S*)\.tar\.gz

View File

@ -1,45 +1,62 @@
package sam3
package i2pkeys
import (
"bytes"
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"errors"
"io"
"strings"
)
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
// 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()
// a certificate. String() returns you the full content of I2PKeys and Addr()
// returns the public keys.
type I2PKeys struct {
addr I2PAddr // only the public key
both string // both public and private keys
Address I2PAddr // only the public key
Both string // both public and private keys
}
// Creates I2PKeys from an I2PAddr and a public/private keypair string (as
// Creates I2PKeys from an I2PAddr and a public/private keypair string (as
// generated by String().)
func NewKeys(addr I2PAddr, both string) I2PKeys {
return I2PKeys{addr, both}
}
// load keys from non standard format
func LoadKeysIncompat(r io.Reader) (k I2PKeys, err error) {
var buff bytes.Buffer
_, err = io.Copy(&buff, r)
if err == nil {
parts := strings.Split(buff.String(), "\n")
k = I2PKeys{I2PAddr(parts[0]), parts[1]}
}
return
}
// store keys in non standard format
func StoreKeysIncompat(k I2PKeys, w io.Writer) (err error) {
_, err = io.WriteString(w, k.Address.Base64()+"\n"+k.Both)
return
}
// Returns the public keys of the I2PKeys.
func (k I2PKeys) Addr() I2PAddr {
return k.addr
return k.Address
}
// Returns the keys (both public and private), in I2Ps base64 format. Use this
// when you create sessions.
func (k I2PKeys) String() string {
return k.both
return k.Both
}
// I2PAddr represents an I2P destination, almost equivalent to an IP address.
@ -48,6 +65,33 @@ func (k I2PKeys) String() string {
// the details of exactly what it is. Read the I2P specifications for more info.)
type I2PAddr string
// an i2p destination hash, the .b32.i2p address if you will
type I2PDestHash [32]byte
// create a desthash from a string b32.i2p address
func DestHashFromString(str string) (dhash I2PDestHash, err error) {
if strings.HasSuffix(str, ".b32.i2p") && len(str) == 60 {
// valid
_, err = i2pB32enc.Decode(dhash[:], []byte(str[:52]+"===="))
} else {
// invalid
err = errors.New("invalid desthash format")
}
return
}
// get string representation of i2p dest hash
func (h I2PDestHash) String() string {
b32addr := make([]byte, 56)
i2pB32enc.Encode(b32addr, h[:])
return string(b32addr[:52]) + ".b32.i2p"
}
// Returns "I2P"
func (h *I2PDestHash) Network() string {
return "I2P"
}
// Returns the base64 representation of the I2PAddr
func (a I2PAddr) Base64() string {
return string(a)
@ -66,6 +110,14 @@ func (a I2PAddr) Network() string {
// 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 NewI2PAddrFromString(addr string) (I2PAddr, error) {
if strings.HasSuffix(addr, ".i2p") {
if strings.HasSuffix(addr, ".b32.i2p") {
return I2PAddr(""), errors.New("cannot convert .b32.i2p to full destination")
}
// strip off .i2p if it's there
addr = addr[:len(addr)-4]
}
addr = strings.Trim(addr, "\t\n\r\f ")
// very basic check
if len(addr) > 4096 || len(addr) < 516 {
return I2PAddr(""), errors.New("Not an I2P address")
@ -77,6 +129,15 @@ func NewI2PAddrFromString(addr string) (I2PAddr, error) {
return I2PAddr(addr), nil
}
func FiveHundredAs() I2PAddr {
s := ""
for x := 0; x < 517; x++ {
s += "A"
}
r, _ := NewI2PAddrFromString(s)
return r
}
// Creates a new I2P address from a byte array. The inverse of ToBytes().
func NewI2PAddrFromBytes(addr []byte) (I2PAddr, error) {
if len(addr) > 4096 || len(addr) < 384 {
@ -96,20 +157,28 @@ func (addr I2PAddr) ToBytes() ([]byte, error) {
return buf, nil
}
// 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
func (addr I2PAddr) Bytes() []byte {
b, _ := addr.ToBytes()
return b
}
// 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 (addr I2PAddr) Base32() string {
func (addr I2PAddr) Base32() (str string) {
return addr.DestHash().String()
}
func (addr I2PAddr) DestHash() (h I2PDestHash) {
hash := sha256.New()
b, _ := addr.ToBytes()
hash.Write(b)
digest := hash.Sum(nil)
b32addr := make([]byte, 56)
i2pB32enc.Encode(b32addr, digest)
return string(b32addr[:52]) + ".b32.i2p"
copy(h[:], digest)
return
}
// Makes any string into a *.b32.i2p human-readable I2P address. This makes no

34
raw.go
View File

@ -6,26 +6,28 @@ import (
"net"
"strconv"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
)
// The RawSession provides no authentication of senders, and there is no sender
// address attached to datagrams, so all communication is anonymous. The
// 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
// 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..
type RawSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys i2pkeys.I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// 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 (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort int) (*RawSession, error) {
func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*RawSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
@ -37,7 +39,7 @@ func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort i
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost + ":0")
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
return nil, err
}
@ -50,7 +52,7 @@ func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort i
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost + ":" + strconv.Itoa(udpPort))
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
return nil, err
}
@ -62,7 +64,7 @@ func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort i
return &RawSession{s.address, id, conn, udpconn, keys, rUDPAddr}, nil
}
// Reads one raw datagram sent to the destination of the DatagramSession. Returns
// 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 (s *RawSession) Read(b []byte) (n int, err error) {
@ -83,7 +85,7 @@ func (s *RawSession) Read(b []byte) (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.
func (s *RawSession) WriteTo(b []byte, addr I2PAddr) (n int, err error) {
func (s *RawSession) WriteTo(b []byte, addr i2pkeys.I2PAddr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
@ -101,7 +103,7 @@ func (s *RawSession) Close() error {
}
// Returns the local I2P destination of the RawSession.
func (s *RawSession) LocalAddr() I2PAddr {
func (s *RawSession) LocalAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
}
@ -116,5 +118,3 @@ func (s *RawSession) SetReadDeadline(t time.Time) error {
func (s *RawSession) SetWriteDeadline(t time.Time) error {
return s.udpconn.SetWriteDeadline(t)
}

71
resolver.go Normal file
View File

@ -0,0 +1,71 @@
package sam3
import (
"bufio"
"bytes"
"errors"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
)
type SAMResolver struct {
*SAM
}
func NewSAMResolver(parent *SAM) (*SAMResolver, error) {
var s SAMResolver
s.SAM = parent
return &s, nil
}
func NewFullSAMResolver(address string) (*SAMResolver, error) {
var s SAMResolver
var err error
s.SAM, err = NewSAM(address)
if err != nil {
return nil, err
}
return &s, nil
}
// Performs a lookup, probably this order: 1) routers known addresses, cached
// addresses, 3) by asking peers in the I2P network.
func (sam *SAMResolver) Resolve(name string) (i2pkeys.I2PAddr, error) {
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\n")); err != nil {
sam.Close()
return i2pkeys.I2PAddr(""), err
}
buf := make([]byte, 4096)
n, err := sam.conn.Read(buf)
if err != nil {
sam.Close()
return i2pkeys.I2PAddr(""), err
}
if n <= 13 || !strings.HasPrefix(string(buf[:n]), "NAMING REPLY ") {
return i2pkeys.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 i2pkeys.I2PAddr(text[6:]), nil
} else if strings.HasPrefix(text, "MESSAGE=") {
errStr += " " + text[8:]
} else {
continue
}
}
return i2pkeys.I2PAddr(""), errors.New(errStr)
}

203
sam3.go
View File

@ -4,17 +4,22 @@ package sam3
import (
"bufio"
"bytes"
"net"
"errors"
"io"
"net"
"os"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
)
// Used for controlling I2Ps SAMv3.
type SAM struct {
address string // ipv4:port
conn net.Conn
address string
conn net.Conn
resolver *SAMResolver
keys *i2pkeys.I2PKeys
sigType int
}
const (
@ -25,116 +30,178 @@ const (
session_I2P_ERROR = "SESSION STATUS RESULT=I2P_ERROR MESSAGE="
)
const (
Sig_NONE = ""
Sig_DSA_SHA1 = "SIGNATURE_TYPE=DSA_SHA1"
Sig_ECDSA_SHA256_P256 = "SIGNATURE_TYPE=ECDSA_SHA256_P256"
Sig_ECDSA_SHA384_P384 = "SIGNATURE_TYPE=ECDSA_SHA384_P384"
Sig_ECDSA_SHA512_P521 = "SIGNATURE_TYPE=ECDSA_SHA512_P521"
Sig_EdDSA_SHA512_Ed25519 = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
)
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
conn, err := net.Dial("tcp4", address)
var s SAM
// TODO: clean this up
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
}
if _, err := conn.Write([]byte("HELLO VERSION MIN=3.0 MAX=3.0\n")); err != nil {
if _, err := conn.Write([]byte("HELLO VERSION MIN=3.0 MAX=3.1\n")); err != nil {
conn.Close()
return nil, err
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
if string(buf[:n]) == "HELLO REPLY RESULT=OK VERSION=3.0\n" {
return &SAM{address, conn}, nil
if strings.Contains(string(buf[:n]), "HELLO REPLY RESULT=OK") {
s.address = address
s.conn = conn
s.keys = nil
s.resolver, err = NewSAMResolver(&s)
if err != nil {
return nil, err
}
return &s, nil
//return &SAM{address, conn, nil, nil}, nil
} else if string(buf[:n]) == "HELLO REPLY RESULT=NOVERSION\n" {
conn.Close()
return nil, errors.New("That SAM bridge does not support SAMv3.")
} else {
conn.Close()
return nil, errors.New(string(buf[:n]))
}
}
func (sam *SAM) Keys() (k *i2pkeys.I2PKeys) {
//TODO: copy them?
k = sam.keys
return
}
// read public/private keys from an io.Reader
func (sam *SAM) ReadKeys(r io.Reader) (err error) {
var keys i2pkeys.I2PKeys
keys, err = i2pkeys.LoadKeysIncompat(r)
if err == nil {
sam.keys = &keys
}
return
}
// if keyfile fname does not exist
func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
if fname == "" {
// transient
keys, err = sam.NewKeys()
if err == nil {
sam.keys = &keys
}
} else {
// persistant
_, err = os.Stat(fname)
if os.IsNotExist(err) {
// make the keys
keys, err = sam.NewKeys()
if err == nil {
sam.keys = &keys
// save keys
var f io.WriteCloser
f, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, 0600)
if err == nil {
err = i2pkeys.StoreKeysIncompat(keys, f)
f.Close()
}
}
} else if err == nil {
// we haz key file
var f *os.File
f, err = os.Open(fname)
if err == nil {
keys, err = i2pkeys.LoadKeysIncompat(f)
if err == nil {
sam.keys = &keys
}
}
}
}
return
}
// 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
// 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 *SAM) NewKeys() (I2PKeys, error) {
if _, err := sam.conn.Write([]byte("DEST GENERATE\n")); err != nil {
return I2PKeys{}, err
func (sam *SAM) NewKeys(sigType ...string) (i2pkeys.I2PKeys, error) {
sigtmp := ""
if len(sigType) > 0 {
sigtmp = sigType[0]
}
if _, err := sam.conn.Write([]byte("DEST GENERATE " + sigtmp + "\n")); err != nil {
return i2pkeys.I2PKeys{}, err
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return I2PKeys{}, err
return i2pkeys.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
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 I2PKeys{}, errors.New("Failed to parse keys.")
return i2pkeys.I2PKeys{}, errors.New("Failed to parse keys.")
}
}
return I2PKeys{I2PAddr(pub), priv}, nil
return i2pkeys.I2PKeys{i2pkeys.I2PAddr(pub), priv}, nil
}
// Performs a lookup, probably this order: 1) routers known addresses, cached
// addresses, 3) by asking peers in the I2P network.
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, 4096)
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 {
continue
}
}
return I2PAddr(""), errors.New(errStr)
func (sam *SAM) Lookup(name string) (i2pkeys.I2PAddr, error) {
return sam.resolver.Resolve(name)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// 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. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) newGenericSession(style, id string, keys I2PKeys, options []string, extras []string) (net.Conn, error) {
func (sam *SAM) newGenericSession(style, id string, keys i2pkeys.I2PKeys, options []string, extras []string) (net.Conn, error) {
return sam.newGenericSessionWithSignature(style, id, keys, Sig_NONE, options, extras)
}
func (sam *SAM) newGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
return sam.newGenericSessionWithSignatureAndPorts(style, id, "0", "0", keys, sigType, options, extras)
}
// 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. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to string, keys i2pkeys.I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
optStr := ""
for _, opt := range options {
optStr += "OPTION=" + opt + " "
optStr += opt + " "
}
conn := sam.conn
scmsg := []byte("SESSION CREATE STYLE=" + style + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + strings.Join(extras, " ") + "\n")
for m, i:=0, 0; m!=len(scmsg); i++ {
scmsg := []byte("SESSION CREATE STYLE=" + style + " FROM_PORT=" + from + " TO_PORT=" + to + " ID=" + id + " DESTINATION=" + keys.String() + " " + sigType + " " + optStr + strings.Join(extras, " ") + "\n")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
return nil, errors.New("writing to SAM failed")
@ -155,9 +222,10 @@ func (sam *SAM) newGenericSession(style, id string, keys I2PKeys, options []stri
text := string(buf[:n])
if strings.HasPrefix(text, session_OK) {
if keys.String() != text[len(session_OK):len(text)-1] {
conn.Close()
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
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")
@ -178,12 +246,5 @@ func (sam *SAM) newGenericSession(style, id string, keys I2PKeys, options []stri
// close this sam session
func (sam *SAM) Close() error {
if err := sam.conn.Close(); err != nil {
return err
}
return nil
return sam.conn.Close()
}

View File

@ -1,29 +1,25 @@
// +build nettest
package sam3
import (
"fmt"
"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)
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tCreating new keys...")
keys, err := sam.NewKeys()
if err != nil {
@ -31,16 +27,16 @@ func Test_Basic(t *testing.T) {
t.Fail()
} else {
fmt.Println("\tAddress created: " + keys.Addr().Base32())
fmt.Println("\tI2PKeys: " + string(keys.both)[:50] + "(...etc)")
fmt.Println("\tI2PKeys: " + string(keys.Both)[: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())
@ -48,7 +44,6 @@ func Test_Basic(t *testing.T) {
}
}
/*
func Test_GenericSession(t *testing.T) {
if testing.Short() {
@ -95,13 +90,6 @@ func Test_GenericSession(t *testing.T) {
}
*/
func Test_RawServerClient(t *testing.T) {
if testing.Short() {
return
@ -127,7 +115,7 @@ func Test_RawServerClient(t *testing.T) {
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan(bool)) {
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
@ -149,17 +137,17 @@ func Test_RawServerClient(t *testing.T) {
fmt.Println("\tClient: Tries to send raw datagram to server")
for {
select {
default :
_, err = rs2.WriteTo([]byte("Hello raw-world! <3 <3 <3 <3 <3 <3"), rs.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send raw datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w :
fmt.Println("\tClient: Sent raw datagram, quitting.")
default:
_, err = rs2.WriteTo([]byte("Hello raw-world! <3 <3 <3 <3 <3 <3"), rs.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send raw datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent raw datagram, quitting.")
return
}
}
c <- true
@ -174,7 +162,5 @@ func Test_RawServerClient(t *testing.T) {
return
}
fmt.Println("\tServer: Received datagram: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}

360
stream.go
View File

@ -3,198 +3,302 @@ package sam3
import (
"bufio"
"bytes"
"context"
"errors"
"io"
"net"
"strconv"
"strings"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
)
// 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
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam
keys i2pkeys.I2PKeys // i2p destination keys
Timeout time.Duration
Deadline time.Time
sigType string
from string
to string
}
func (ss *StreamSession) From() string {
return ss.from
}
func (ss *StreamSession) To() string {
return ss.to
}
func (ss *StreamSession) SignatureType() string {
return ss.sigType
}
// Returns the local tunnel name of the I2P tunnel used for the stream session
func (ss StreamSession) ID() string {
func (ss *StreamSession) ID() string {
return ss.id
}
func (ss *StreamSession) Close() error {
return ss.conn.Close()
}
// Returns the I2P destination (the address) of the stream session
func (ss StreamSession) Addr() I2PAddr {
func (ss *StreamSession) Addr() i2pkeys.I2PAddr {
return ss.keys.Addr()
}
// Returns the keys associated with the stream session
func (ss StreamSession) Keys() I2PKeys {
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
return ss.keys
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error) {
func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
if err != nil {
return nil, err
}
return &StreamSession{sam.address, id, conn, keys}, nil
return &StreamSession{sam.address, id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
conn, err := sam.newGenericSessionWithSignature("STREAM", id, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
return &StreamSession{sam.address, id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, "0", "0"}, nil
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
conn, err := sam.newGenericSessionWithSignatureAndPorts("STREAM", id, from, to, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
return &StreamSession{sam.address, id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, from, to}, nil
}
// lookup name, convienence function
func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
sam, err := NewSAM(s.samAddr)
if err == nil {
addr, err := sam.Lookup(name)
sam.Close()
return addr, err
}
return i2pkeys.I2PAddr(""), err
}
// context-aware dialer, eventually...
func (s *StreamSession) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
return s.DialContextI2P(ctx, n, addr)
}
// context-aware dialer, eventually...
func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SAMConn, error) {
if ctx == nil {
panic("nil context")
}
deadline := s.deadline(ctx, time.Now())
if !deadline.IsZero() {
if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
subCtx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
ctx = subCtx
}
}
i2paddr, err := i2pkeys.NewI2PAddrFromString(addr)
if err != nil {
return nil, err
}
return s.DialI2P(i2paddr)
}
/*
func (s *StreamSession) Cancel() chan *StreamSession {
ch := make(chan *StreamSession)
ch <- s
return ch
}*/
func minNonzeroTime(a, b time.Time) time.Time {
if a.IsZero() {
return b
}
if b.IsZero() || a.Before(b) {
return a
}
return b
}
// deadline returns the earliest of:
// - now+Timeout
// - d.Deadline
// - the context's deadline
// Or zero, if none of Timeout, Deadline, or context's deadline is set.
func (s *StreamSession) deadline(ctx context.Context, now time.Time) (earliest time.Time) {
if s.Timeout != 0 { // including negative, for historical reasons
earliest = now.Add(s.Timeout)
}
if d, ok := ctx.Deadline(); ok {
earliest = minNonzeroTime(earliest, d)
}
return minNonzeroTime(earliest, s.Deadline)
}
// implement net.Dialer
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
var i2paddr i2pkeys.I2PAddr
var host string
host, _, err = net.SplitHostPort(addr)
if err == nil {
// check for name
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
// name lookup
i2paddr, err = s.Lookup(host)
} else {
// probably a destination
i2paddr = i2pkeys.I2PAddr(host)
}
if err == nil {
return s.DialI2P(i2paddr)
}
}
return
}
// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error) {
func (s *StreamSession) DialI2P(addr i2pkeys.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"))
_, err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " FROM_PORT=" + s.from + " TO_PORT=" + s.to + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
if err != nil {
conn.Close()
return nil, err
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
if err != nil && err != io.EOF {
conn.Close()
return nil, err
}
scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
switch scanner.Text() {
case "STREAM" :
case "STREAM":
continue
case "STATUS" :
case "STATUS":
continue
case "RESULT=OK" :
return &SAMConn{s.keys.addr, addr, conn}, nil
case "RESULT=CANT_REACH_PEER" :
case "RESULT=OK":
return &SAMConn{s.keys.Address, addr, conn}, nil
case "RESULT=CANT_REACH_PEER":
conn.Close()
return nil, errors.New("Can not reach peer")
case "RESULT=I2P_ERROR" :
case "RESULT=I2P_ERROR":
conn.Close()
return nil, errors.New("I2P internal error")
case "RESULT=INVALID_KEY" :
case "RESULT=INVALID_KEY":
conn.Close()
return nil, errors.New("Invalid key")
case "RESULT=INVALID_ID" :
case "RESULT=INVALID_ID":
conn.Close()
return nil, errors.New("Invalid tunnel ID")
case "RESULT=TIMEOUT" :
case "RESULT=TIMEOUT":
conn.Close()
return nil, errors.New("Timeout")
default :
default:
conn.Close()
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.
// create a new stream listener to accept inbound connections
func (s *StreamSession) Listen() (*StreamListener, error) {
sam, err := NewSAM(s.conn.RemoteAddr().String())
if err != nil {
return nil, err
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
sam.Close()
return nil, err
}
listener, err := net.Listen("tcp4", lhost + ":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()")
return &StreamListener{
session: s,
id: s.id,
laddr: s.keys.Addr(),
}, nil
}
// Implements net.Listener for I2P streaming sessions
type StreamListener struct {
conn net.Conn
listener net.Listener
lport int
laddr I2PAddr
// parent stream session
session *StreamSession
// our session id
id string
// our local address for this sam socket
laddr i2pkeys.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
}
// Closes the stream session. Implements net.Listener
func (l *StreamListener) Close() error {
err := l.listener.Close()
err2 := l.conn.Close()
if err2 != nil {
return err2
}
return err
}
// Returns the I2P destination (address) of the stream session. Implements net.Listener
// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
return l.laddr
}
// implements net.Listener
func (l *StreamListener) Close() error {
return l.session.Close()
}
// implements net.Listener
func (l *StreamListener) Accept() (net.Conn, error) {
return l.AcceptI2P()
}
// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
s, err := NewSAM(l.session.samAddr)
if err == nil {
// we connected to sam
// send accept() command
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
// read reply
rd := bufio.NewReader(s.conn)
// read first line
line, err := rd.ReadString(10)
if err == nil {
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
dest, err := rd.ReadString(10)
if err == nil {
// return wrapped connection
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {
s.Close()
return nil, err
}
} else {
s.Close()
return nil, errors.New("invalid sam line: " + line)
}
} else {
s.Close()
return nil, err
}
}
s.Close()
return nil, err
}

View File

@ -1,11 +1,14 @@
// +build nettest
package sam3
import (
"fmt"
"strings"
"testing"
)
"github.com/eyedeekay/sam3/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {
if testing.Short() {
@ -14,7 +17,7 @@ func Test_StreamingDial(t *testing.T) {
fmt.Println("Test_StreamingDial")
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error)
fmt.Println(err.Error())
t.Fail()
return
}
@ -33,14 +36,14 @@ func Test_StreamingDial(t *testing.T) {
return
}
fmt.Println("\tNotice: This may fail if your I2P node is not well integrated in the I2P network.")
fmt.Println("\tLooking up forum.i2p")
forumAddr, err := sam.Lookup("forum.i2p")
fmt.Println("\tLooking up i2p-projekt.i2p")
forumAddr, err := sam.Lookup("i2p-projekt.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tDialing forum.i2p")
fmt.Println("\tDialing i2p-projekt.i2p")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
@ -52,14 +55,14 @@ func Test_StreamingDial(t *testing.T) {
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
t.Fail()
return
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)
fmt.Printf("\tProbably failed to StreamSession.DialI2P(i2p-projekt.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("\tRead HTTP/HTML from forum.i2p")
fmt.Println("\tRead HTTP/HTML from i2p-projekt.i2p")
}
}
@ -67,7 +70,7 @@ func Test_StreamingServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
@ -86,7 +89,7 @@ func Test_StreamingServerClient(t *testing.T) {
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan(bool)) {
go func(c, w chan (bool)) {
if !(<-w) {
return
}
@ -140,18 +143,17 @@ func Test_StreamingServerClient(t *testing.T) {
}
defer conn.Close()
buf := make([]byte, 512)
n,err := conn.Read(buf)
n, err := conn.Read(buf)
fmt.Printf("\tClient exited successfully: %t\n", <-c)
fmt.Println("\tServer: received from Client: " + string(buf[:n]))
}
func ExampleStreamSession() {
// 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())
@ -174,7 +176,7 @@ func ExampleStreamSession() {
fmt.Println(err.Error())
return
}
conn, err := ss.DialI2P(someone)
if err != nil {
fmt.Println(err.Error())
@ -184,7 +186,7 @@ func ExampleStreamSession() {
fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
return
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
@ -194,7 +196,7 @@ func ExampleStreamSession() {
fmt.Println("Read HTTP/HTML from zzz.i2p")
}
return
// Output:
//Sending HTTP GET /
//Read HTTP/HTML from zzz.i2p
@ -204,7 +206,7 @@ func ExampleStreamListener() {
// 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)
@ -213,7 +215,7 @@ func ExampleStreamListener() {
return
}
defer sam.Close()
keys, err := sam.NewKeys()
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
@ -222,14 +224,14 @@ func ExampleStreamListener() {
quit := make(chan bool)
// Client connecting to the server
go func(server I2PAddr) {
go func(server i2pkeys.I2PAddr) {
csam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer csam.Close()
keys, err := csam.NewKeys()
keys, err := csam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
@ -255,7 +257,7 @@ func ExampleStreamListener() {
}
fmt.Println(string(buf[:n]))
quit <- true
}(keys.Addr()) // end of client
}(keys.Addr()) // end of client
ss, err := sam.NewStreamSession("server_example", keys, Options_Small)
if err != nil {
@ -273,9 +275,9 @@ func ExampleStreamListener() {
return
}
conn.Write([]byte("Hello world!"))
<-quit // waits for client to die, for example only
// Output:
//Hello world!
}

View File

@ -2,39 +2,39 @@ package sam3
// Examples and suggestions for options when creating sessions.
var (
// Suitable options if you are shuffling A LOT of traffic. If unused, this
// 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.
"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"}
"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"}
"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
// 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"}
"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"}
"inbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2"}
)