Compare commits
103 Commits
merge-from
...
v0.32.31
Author | SHA1 | Date | |
---|---|---|---|
![]() |
529cdd6063 | ||
![]() |
33f3caa3f9 | ||
![]() |
5a43ad260d | ||
![]() |
cd744bfe49 | ||
![]() |
1ef2d90f46 | ||
![]() |
f90e3b2755 | ||
![]() |
a959ac58ba | ||
![]() |
eecb73010c | ||
![]() |
442114200c | ||
![]() |
d4ac38fc97 | ||
![]() |
0a2d58418f | ||
![]() |
ca1a4f7f54 | ||
![]() |
6406efd277 | ||
![]() |
a12f9ba498 | ||
![]() |
4a24538cd5 | ||
![]() |
f617388473 | ||
![]() |
c2b2b39c74 | ||
![]() |
0067d37ca3 | ||
![]() |
f8d54526ea | ||
![]() |
e9868b791e | ||
![]() |
a810f009bd | ||
![]() |
99ad6522eb | ||
![]() |
6f6be01fb2 | ||
![]() |
7438f855cd | ||
![]() |
eabe42f33f | ||
![]() |
0e9665027e | ||
![]() |
79b526d69c | ||
![]() |
5b1dcc54aa | ||
![]() |
6b863d7514 | ||
![]() |
9ee74a89d3 | ||
![]() |
6029d7dd6e | ||
![]() |
265e8b21b4 | ||
![]() |
9d24d56c28 | ||
![]() |
d622765b79 | ||
![]() |
57c0d7fc54 | ||
![]() |
777c148eee | ||
![]() |
2b8b539e44 | ||
![]() |
c30f54fc7b | ||
![]() |
d09fa462ed | ||
![]() |
4a6b7966f2 | ||
![]() |
d1d27c55aa | ||
![]() |
d422604759 | ||
![]() |
1e3684b26e | ||
![]() |
917c6a7c49 | ||
![]() |
a42f47931b | ||
![]() |
8ed385c394 | ||
![]() |
3ae3281168 | ||
![]() |
ee0fb2c871 | ||
![]() |
a6914d7d9b | ||
![]() |
af5a3f3239 | ||
![]() |
2e872c6b20 | ||
![]() |
337bae0956 | ||
![]() |
ab5663c157 | ||
![]() |
554202709f | ||
![]() |
d1a21d339f | ||
![]() |
07ebb0f823 | ||
![]() |
21b597c509 | ||
![]() |
93ee7a66e9 | ||
![]() |
b81d625bea | ||
![]() |
313d32ccf3 | ||
![]() |
4065aba3ee | ||
![]() |
5df47060c9 | ||
![]() |
aebf5a4245 | ||
![]() |
acc809d485 | ||
![]() |
0152ed864d | ||
![]() |
c77a5d980c | ||
![]() |
d8ee0fca12 | ||
![]() |
4babdcd9f7 | ||
![]() |
4949b11b57 | ||
![]() |
c1321ac144 | ||
![]() |
f7d729017d | ||
![]() |
f3a993315a | ||
16e9417d2e | |||
![]() |
d0e367fbab | ||
![]() |
090712478f | ||
![]() |
5233589f4a | ||
![]() |
700e096281 | ||
![]() |
7edd91a383 | ||
![]() |
fcc4565138 | ||
![]() |
60182b22f8 | ||
![]() |
59f815f2e7 | ||
![]() |
e6936eed2f | ||
![]() |
99b4ecf533 | ||
![]() |
1d0d324d13 | ||
![]() |
9bbc5249e1 | ||
![]() |
5a0f4e39dc | ||
![]() |
3247769471 | ||
![]() |
87e20ae658 | ||
![]() |
0143d1df57 | ||
![]() |
b8a2f40bf1 | ||
![]() |
b51a8977cf | ||
![]() |
298b55c427 | ||
![]() |
92493aa92a | ||
![]() |
6f95da72ea | ||
![]() |
c6eba7d0a0 | ||
![]() |
b500865ffe | ||
![]() |
20095ae901 | ||
![]() |
13894c0a14 | ||
![]() |
d32071ba2e | ||
![]() |
a30f0eded7 | ||
![]() |
937d862d90 | ||
![]() |
a1d2ac25d6 | ||
![]() |
4108184369 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
*~
|
||||
*.swp
|
||||
README.md.asc
|
||||
|
24
LICENSE
Normal file
24
LICENSE
Normal 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/>
|
27
Makefile
Normal file
27
Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
USER_GH=eyedeekay
|
||||
VERSION=0.32.31
|
||||
packagename=sam3
|
||||
|
||||
echo:
|
||||
@echo "type make version to do release $(VERSION)"
|
||||
|
||||
version:
|
||||
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
|
||||
|
||||
del:
|
||||
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
|
||||
|
||||
tar:
|
||||
tar --exclude .git \
|
||||
--exclude .go \
|
||||
--exclude bin \
|
||||
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
|
||||
|
||||
copier:
|
||||
echo '#! /usr/bin/env sh' > deb/copy.sh
|
||||
echo 'for f in $$(ls); do scp $$f/*.deb user@192.168.99.106:~/DEBIAN_PKGS/$$f/main/; done' >> deb/copy.sh
|
||||
|
||||
fmt:
|
||||
find . -name '*.go' -exec gofmt -w -s {} \;
|
||||
|
10
README.md
10
README.md
@@ -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
105
README.md.asc
Normal 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-----
|
44
SAMConn.go
44
SAMConn.go
@@ -1,57 +1,69 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
import (
|
||||
. "github.com/eyedeekay/sam3/i2pkeys"
|
||||
)
|
||||
*/
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
|
363
config.go
Normal file
363
config.go
Normal file
@@ -0,0 +1,363 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
)
|
||||
|
||||
// I2PConfig is a struct which manages I2P configuration options
|
||||
type I2PConfig struct {
|
||||
SamHost string
|
||||
SamPort string
|
||||
TunName string
|
||||
|
||||
SamMin string
|
||||
SamMax string
|
||||
|
||||
Fromport string
|
||||
Toport string
|
||||
|
||||
Style string
|
||||
TunType string
|
||||
|
||||
DestinationKeys i2pkeys.I2PKeys
|
||||
|
||||
SigType string
|
||||
EncryptLeaseSet string
|
||||
LeaseSetKey string
|
||||
LeaseSetPrivateKey string
|
||||
LeaseSetPrivateSigningKey string
|
||||
LeaseSetKeys i2pkeys.I2PKeys
|
||||
InAllowZeroHop string
|
||||
OutAllowZeroHop string
|
||||
InLength string
|
||||
OutLength string
|
||||
InQuantity string
|
||||
OutQuantity string
|
||||
InVariance string
|
||||
OutVariance string
|
||||
InBackupQuantity string
|
||||
OutBackupQuantity string
|
||||
FastRecieve string
|
||||
UseCompression string
|
||||
MessageReliability string
|
||||
CloseIdle string
|
||||
CloseIdleTime string
|
||||
ReduceIdle string
|
||||
ReduceIdleTime string
|
||||
ReduceIdleQuantity string
|
||||
//Streaming Library options
|
||||
AccessListType string
|
||||
AccessList []string
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Sam() string {
|
||||
host := "127.0.0.1"
|
||||
port := "7656"
|
||||
if f.SamHost != "" {
|
||||
host = f.SamHost
|
||||
}
|
||||
if f.SamPort != "" {
|
||||
port = f.SamPort
|
||||
}
|
||||
return host + ":" + port
|
||||
}
|
||||
|
||||
func (f *I2PConfig) SetSAMAddress(addr string) {
|
||||
hp := strings.Split(addr, ":")
|
||||
if len(hp) == 1 {
|
||||
f.SamHost = hp[0]
|
||||
} else if len(hp) == 2 {
|
||||
f.SamPort = hp[1]
|
||||
f.SamHost = hp[0]
|
||||
}
|
||||
f.SamPort = "7656"
|
||||
f.SamHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
func (f *I2PConfig) ID() string {
|
||||
if f.TunName == "" {
|
||||
b := make([]byte, 12)
|
||||
for i := range b {
|
||||
b[i] = "abcdefghijklmnopqrstuvwxyz"[rand.Intn(len("abcdefghijklmnopqrstuvwxyz"))]
|
||||
}
|
||||
f.TunName = string(b)
|
||||
}
|
||||
return " ID=" + f.TunName + " "
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Leasesetsettings() (string, string, string) {
|
||||
var r, s, t string
|
||||
if f.LeaseSetKey != "" {
|
||||
r = " i2cp.leaseSetKey=" + f.LeaseSetKey + " "
|
||||
}
|
||||
if f.LeaseSetPrivateKey != "" {
|
||||
s = " i2cp.leaseSetPrivateKey=" + f.LeaseSetPrivateKey + " "
|
||||
}
|
||||
if f.LeaseSetPrivateSigningKey != "" {
|
||||
t = " i2cp.leaseSetPrivateSigningKey=" + f.LeaseSetPrivateSigningKey + " "
|
||||
}
|
||||
return r, s, t
|
||||
}
|
||||
|
||||
func (f *I2PConfig) FromPort() string {
|
||||
if f.samMax() < 3.1 {
|
||||
return ""
|
||||
}
|
||||
if f.Fromport != "0" {
|
||||
return " FROM_PORT=" + f.Fromport + " "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) ToPort() string {
|
||||
if f.samMax() < 3.1 {
|
||||
return ""
|
||||
}
|
||||
if f.Toport != "0" {
|
||||
return " TO_PORT=" + f.Toport + " "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) SessionStyle() string {
|
||||
if f.Style != "" {
|
||||
return " STYLE=" + f.Style + " "
|
||||
}
|
||||
return " STYLE=STREAM "
|
||||
}
|
||||
|
||||
func (f *I2PConfig) samMax() float64 {
|
||||
i, err := strconv.Atoi(f.SamMax)
|
||||
if err != nil {
|
||||
return 3.1
|
||||
}
|
||||
return float64(i)
|
||||
}
|
||||
|
||||
func (f *I2PConfig) MinSAM() string {
|
||||
if f.SamMin == "" {
|
||||
return "3.0"
|
||||
}
|
||||
return f.SamMin
|
||||
}
|
||||
|
||||
func (f *I2PConfig) MaxSAM() string {
|
||||
if f.SamMax == "" {
|
||||
return "3.1"
|
||||
}
|
||||
return f.SamMax
|
||||
}
|
||||
|
||||
func (f *I2PConfig) DestinationKey() string {
|
||||
if &f.DestinationKeys != nil {
|
||||
return " DESTINATION=" + f.DestinationKeys.String() + " "
|
||||
}
|
||||
return " DESTINATION=TRANSIENT "
|
||||
}
|
||||
|
||||
func (f *I2PConfig) SignatureType() string {
|
||||
if f.samMax() < 3.1 {
|
||||
return ""
|
||||
}
|
||||
if f.SigType != "" {
|
||||
return " SIGNATURE_TYPE=" + f.SigType + " "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) EncryptLease() string {
|
||||
if f.EncryptLeaseSet == "true" {
|
||||
return " i2cp.encryptLeaseSet=true "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Reliability() string {
|
||||
if f.MessageReliability != "" {
|
||||
return " i2cp.messageReliability=" + f.MessageReliability + " "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Reduce() string {
|
||||
if f.ReduceIdle == "true" {
|
||||
return "i2cp.reduceOnIdle=" + f.ReduceIdle + "i2cp.reduceIdleTime=" + f.ReduceIdleTime + "i2cp.reduceQuantity=" + f.ReduceIdleQuantity
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Close() string {
|
||||
if f.CloseIdle == "true" {
|
||||
return "i2cp.closeOnIdle=" + f.CloseIdle + "i2cp.closeIdleTime=" + f.CloseIdleTime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) DoZero() string {
|
||||
r := ""
|
||||
if f.InAllowZeroHop == "true" {
|
||||
r += " inbound.allowZeroHop=" + f.InAllowZeroHop + " "
|
||||
}
|
||||
if f.OutAllowZeroHop == "true" {
|
||||
r += " outbound.allowZeroHop= " + f.OutAllowZeroHop + " "
|
||||
}
|
||||
if f.FastRecieve == "true" {
|
||||
r += " " + f.FastRecieve + " "
|
||||
}
|
||||
return r
|
||||
}
|
||||
func (f *I2PConfig) Print() []string {
|
||||
lsk, lspk, lspsk := f.Leasesetsettings()
|
||||
return []string{
|
||||
//f.targetForPort443(),
|
||||
"inbound.length=" + f.InLength,
|
||||
"outbound.length=" + f.OutLength,
|
||||
"inbound.lengthVariance=" + f.InVariance,
|
||||
"outbound.lengthVariance=" + f.OutVariance,
|
||||
"inbound.backupQuantity=" + f.InBackupQuantity,
|
||||
"outbound.backupQuantity=" + f.OutBackupQuantity,
|
||||
"inbound.quantity=" + f.InQuantity,
|
||||
"outbound.quantity=" + f.OutQuantity,
|
||||
f.DoZero(),
|
||||
//"i2cp.fastRecieve=" + f.FastRecieve,
|
||||
"i2cp.gzip=" + f.UseCompression,
|
||||
f.Reduce(),
|
||||
f.Close(),
|
||||
f.Reliability(),
|
||||
f.EncryptLease(),
|
||||
lsk, lspk, lspsk,
|
||||
f.Accesslisttype(),
|
||||
f.Accesslist(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Accesslisttype() string {
|
||||
if f.AccessListType == "whitelist" {
|
||||
return "i2cp.enableAccessList=true"
|
||||
} else if f.AccessListType == "blacklist" {
|
||||
return "i2cp.enableBlackList=true"
|
||||
} else if f.AccessListType == "none" {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *I2PConfig) Accesslist() string {
|
||||
if f.AccessListType != "" && len(f.AccessList) > 0 {
|
||||
r := ""
|
||||
for _, s := range f.AccessList {
|
||||
r += s + ","
|
||||
}
|
||||
return "i2cp.accessList=" + strings.TrimSuffix(r, ",")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func NewConfig(opts ...func(*I2PConfig) error) (*I2PConfig, error) {
|
||||
var config I2PConfig
|
||||
config.SamHost = "127.0.0.1"
|
||||
config.SamPort = "7656"
|
||||
config.SamMin = "3.0"
|
||||
config.SamMax = "3.2"
|
||||
config.TunName = ""
|
||||
config.TunType = "server"
|
||||
config.Style = "STREAM"
|
||||
config.InLength = "3"
|
||||
config.OutLength = "3"
|
||||
config.InQuantity = "2"
|
||||
config.OutQuantity = "2"
|
||||
config.InVariance = "1"
|
||||
config.OutVariance = "1"
|
||||
config.InBackupQuantity = "3"
|
||||
config.OutBackupQuantity = "3"
|
||||
config.InAllowZeroHop = "false"
|
||||
config.OutAllowZeroHop = "false"
|
||||
config.EncryptLeaseSet = "false"
|
||||
config.LeaseSetKey = ""
|
||||
config.LeaseSetPrivateKey = ""
|
||||
config.LeaseSetPrivateSigningKey = ""
|
||||
config.FastRecieve = "false"
|
||||
config.UseCompression = "true"
|
||||
config.ReduceIdle = "false"
|
||||
config.ReduceIdleTime = "15"
|
||||
config.ReduceIdleQuantity = "4"
|
||||
config.CloseIdle = "false"
|
||||
config.CloseIdleTime = "300000"
|
||||
config.MessageReliability = "none"
|
||||
for _, o := range opts {
|
||||
if err := o(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
106
datagram.go
106
datagram.go
@@ -6,24 +6,27 @@ 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
|
||||
remoteAddr *i2pkeys.I2PAddr // optional remote I2P address
|
||||
}
|
||||
|
||||
// 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 +38,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 +51,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
|
||||
}
|
||||
@@ -57,21 +60,30 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr}, nil
|
||||
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil}, nil
|
||||
}
|
||||
|
||||
// Reads one datagram sent to the destination of the DatagramSession. Returns
|
||||
func (s *DatagramSession) B32() string {
|
||||
return s.keys.Addr().Base32()
|
||||
}
|
||||
|
||||
func (s *DatagramSession) RemoteAddr() net.Addr {
|
||||
return s.remoteAddr
|
||||
}
|
||||
|
||||
// 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,32 +92,45 @@ 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
|
||||
func (s *DatagramSession) Accept() (net.Conn, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *DatagramSession) Read(b []byte) (n int, err error) {
|
||||
rint, _, rerr := s.ReadFrom(b)
|
||||
return rint, rerr
|
||||
}
|
||||
|
||||
// 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) {
|
||||
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
|
||||
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
header := []byte("3.1 " + s.id + " " + addr.String() + "\n")
|
||||
msg := append(header, b...)
|
||||
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *DatagramSession) Write(b []byte) (int, error) {
|
||||
return s.WriteTo(b, s.remoteAddr)
|
||||
}
|
||||
|
||||
// Closes the DatagramSession. Implements net.PacketConn
|
||||
func (s *DatagramSession) Close() error {
|
||||
err := s.conn.Close()
|
||||
@@ -116,12 +141,31 @@ 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) Addr() 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 {
|
||||
@@ -138,4 +182,6 @@ func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
|
||||
return s.udpconn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
|
||||
func (s *DatagramSession) SetWriteBuffer(bytes int) error {
|
||||
return s.udpconn.SetWriteBuffer(bytes)
|
||||
}
|
||||
|
@@ -1,12 +1,14 @@
|
||||
// +build nettest
|
||||
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
func Test_DatagramServerClient(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
@@ -24,7 +26,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 +35,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 +54,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 +84,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())
|
||||
@@ -128,6 +125,7 @@ func ExampleDatagramSession() {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
log.Println("Got message: '" + string(buf[:n]) + "'")
|
||||
fmt.Println("Got message: " + string(buf[:n]))
|
||||
|
||||
return
|
||||
@@ -135,3 +133,53 @@ func ExampleDatagramSession() {
|
||||
//Got message: Hello myself!
|
||||
}
|
||||
|
||||
func ExampleMiniDatagramSession() {
|
||||
// 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("MINIDGTUN", 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
|
||||
}
|
||||
err = dg.SetWriteBuffer(14 * 1024)
|
||||
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
|
||||
}
|
||||
log.Println("Got message: '" + string(buf[:n]) + "'")
|
||||
fmt.Println("Got message: " + string(buf[:n]))
|
||||
|
||||
return
|
||||
// Output:
|
||||
//Got message: Hello myself!
|
||||
}
|
||||
|
52
debian/changelog
vendored
Normal file
52
debian/changelog
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
golang-github-eyedeekay-sam3 (0.3.2.31) unreleased; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Incorporate address fix from qiwenmin
|
||||
* Shortcut for saving keys to text file
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sun, 18 Oct 2020 1:01:03 -0500
|
||||
|
||||
golang-github-eyedeekay-sam3 (0.3.2.3) unreleased; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Purge ramp, re-release
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Mon, 22 Jun 2020 17:55:03 -0500
|
||||
|
||||
golang-github-eyedeekay-sam3 (0.3.2.2) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Purge ramp
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sat, 7 Dec 2019 22:05:34 -0500
|
||||
|
||||
|
||||
golang-github-eyedeekay-sam3 (0.3.2.1) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Add support for 44-character destination hashes.
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sat, 7 Dec 2019 17:30:30 -0500
|
||||
|
||||
|
||||
golang-github-eyedeekay-sam3 (0.3.2.01) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* completely remove the old i2pkeys version and replace it with the new one.
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sat, 25 May 2019 14:38:11 -0500
|
||||
|
||||
golang-github-eyedeekay-sam3 (0.3.2.0) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Bug fixes, create stable branch
|
||||
* Move i2pkeys
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sat, 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
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
11
|
25
debian/control
vendored
Normal file
25
debian/control
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
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,
|
||||
git,
|
||||
golang-any | golang-go,
|
||||
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
17
debian/copyright
vendored
Normal 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
|
2
debian/gbp.conf
vendored
Normal file
2
debian/gbp.conf
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[DEFAULT]
|
||||
pristine-tar = True
|
4
debian/rules
vendored
Executable file
4
debian/rules
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=golang --with=golang
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
4
debian/watch
vendored
Normal file
4
debian/watch
vendored
Normal 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
|
369
emit-options.go
Normal file
369
emit-options.go
Normal file
@@ -0,0 +1,369 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//Option is a SAMEmit Option
|
||||
type Option func(*SAMEmit) error
|
||||
|
||||
//SetType sets the type of the forwarder server
|
||||
func SetType(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if s == "STREAM" {
|
||||
c.Style = s
|
||||
return nil
|
||||
} else if s == "DATAGRAM" {
|
||||
c.Style = s
|
||||
return nil
|
||||
} else if s == "RAW" {
|
||||
c.Style = s
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid session STYLE=%s, must be STREAM, DATAGRAM, or RAW", s)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSAMAddress sets the SAM address all-at-once
|
||||
func SetSAMAddress(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
sp := strings.Split(s, ":")
|
||||
if len(sp) > 2 {
|
||||
return fmt.Errorf("Invalid address string: %s", sp)
|
||||
}
|
||||
if len(sp) == 2 {
|
||||
c.I2PConfig.SamPort = sp[1]
|
||||
}
|
||||
c.I2PConfig.SamHost = sp[0]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetSAMHost sets the host of the SAMEmit's SAM bridge
|
||||
func SetSAMHost(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.SamHost = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetSAMPort sets the port of the SAMEmit's SAM bridge using a string
|
||||
func SetSAMPort(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
port, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid SAM Port %s; non-number", s)
|
||||
}
|
||||
if port < 65536 && port > -1 {
|
||||
c.I2PConfig.SamPort = s
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
//SetName sets the host of the SAMEmit's SAM bridge
|
||||
func SetName(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.TunName = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetInLength sets the number of hops inbound
|
||||
func SetInLength(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u < 7 && u >= 0 {
|
||||
c.I2PConfig.InLength = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid inbound tunnel length")
|
||||
}
|
||||
}
|
||||
|
||||
//SetOutLength sets the number of hops outbound
|
||||
func SetOutLength(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u < 7 && u >= 0 {
|
||||
c.I2PConfig.OutLength = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid outbound tunnel length")
|
||||
}
|
||||
}
|
||||
|
||||
//SetInVariance sets the variance of a number of hops inbound
|
||||
func SetInVariance(i int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if i < 7 && i > -7 {
|
||||
c.I2PConfig.InVariance = strconv.Itoa(i)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid inbound tunnel length")
|
||||
}
|
||||
}
|
||||
|
||||
//SetOutVariance sets the variance of a number of hops outbound
|
||||
func SetOutVariance(i int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if i < 7 && i > -7 {
|
||||
c.I2PConfig.OutVariance = strconv.Itoa(i)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid outbound tunnel variance")
|
||||
}
|
||||
}
|
||||
|
||||
//SetInQuantity sets the inbound tunnel quantity
|
||||
func SetInQuantity(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u <= 16 && u > 0 {
|
||||
c.I2PConfig.InQuantity = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid inbound tunnel quantity")
|
||||
}
|
||||
}
|
||||
|
||||
//SetOutQuantity sets the outbound tunnel quantity
|
||||
func SetOutQuantity(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u <= 16 && u > 0 {
|
||||
c.I2PConfig.OutQuantity = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid outbound tunnel quantity")
|
||||
}
|
||||
}
|
||||
|
||||
//SetInBackups sets the inbound tunnel backups
|
||||
func SetInBackups(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u < 6 && u >= 0 {
|
||||
c.I2PConfig.InBackupQuantity = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid inbound tunnel backup quantity")
|
||||
}
|
||||
}
|
||||
|
||||
//SetOutBackups sets the inbound tunnel backups
|
||||
func SetOutBackups(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u < 6 && u >= 0 {
|
||||
c.I2PConfig.OutBackupQuantity = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid outbound tunnel backup quantity")
|
||||
}
|
||||
}
|
||||
|
||||
//SetEncrypt tells the router to use an encrypted leaseset
|
||||
func SetEncrypt(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.EncryptLeaseSet = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.EncryptLeaseSet = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetLeaseSetKey sets the host of the SAMEmit's SAM bridge
|
||||
func SetLeaseSetKey(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.LeaseSetKey = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetLeaseSetPrivateKey sets the host of the SAMEmit's SAM bridge
|
||||
func SetLeaseSetPrivateKey(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.LeaseSetPrivateKey = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetLeaseSetPrivateSigningKey sets the host of the SAMEmit's SAM bridge
|
||||
func SetLeaseSetPrivateSigningKey(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.LeaseSetPrivateSigningKey = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetMessageReliability sets the host of the SAMEmit's SAM bridge
|
||||
func SetMessageReliability(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.MessageReliability = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetAllowZeroIn tells the tunnel to accept zero-hop peers
|
||||
func SetAllowZeroIn(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.InAllowZeroHop = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.InAllowZeroHop = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetAllowZeroOut tells the tunnel to accept zero-hop peers
|
||||
func SetAllowZeroOut(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.OutAllowZeroHop = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.OutAllowZeroHop = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetCompress tells clients to use compression
|
||||
func SetCompress(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.UseCompression = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.UseCompression = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetFastRecieve tells clients to use compression
|
||||
func SetFastRecieve(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.FastRecieve = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.FastRecieve = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdle tells the connection to reduce it's tunnels during extended idle time.
|
||||
func SetReduceIdle(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.ReduceIdle = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.ReduceIdle = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdleTime sets the time to wait before reducing tunnels to idle levels
|
||||
func SetReduceIdleTime(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.ReduceIdleTime = "300000"
|
||||
if u >= 6 {
|
||||
c.I2PConfig.ReduceIdleTime = strconv.Itoa((u * 60) * 1000)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid reduce idle timeout(Measured in minutes) %v", u)
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdleTimeMs sets the time to wait before reducing tunnels to idle levels in milliseconds
|
||||
func SetReduceIdleTimeMs(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.ReduceIdleTime = "300000"
|
||||
if u >= 300000 {
|
||||
c.I2PConfig.ReduceIdleTime = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid reduce idle timeout(Measured in milliseconds) %v", u)
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdleQuantity sets minimum number of tunnels to reduce to during idle time
|
||||
func SetReduceIdleQuantity(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if u < 5 {
|
||||
c.I2PConfig.ReduceIdleQuantity = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid reduce tunnel quantity")
|
||||
}
|
||||
}
|
||||
|
||||
//SetCloseIdle tells the connection to close it's tunnels during extended idle time.
|
||||
func SetCloseIdle(b bool) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if b {
|
||||
c.I2PConfig.CloseIdle = "true"
|
||||
return nil
|
||||
}
|
||||
c.I2PConfig.CloseIdle = "false"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetCloseIdleTime sets the time to wait before closing tunnels to idle levels
|
||||
func SetCloseIdleTime(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.CloseIdleTime = "300000"
|
||||
if u >= 6 {
|
||||
c.I2PConfig.CloseIdleTime = strconv.Itoa((u * 60) * 1000)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid close idle timeout(Measured in minutes) %v", u)
|
||||
}
|
||||
}
|
||||
|
||||
//SetCloseIdleTimeMs sets the time to wait before closing tunnels to idle levels in milliseconds
|
||||
func SetCloseIdleTimeMs(u int) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
c.I2PConfig.CloseIdleTime = "300000"
|
||||
if u >= 300000 {
|
||||
c.I2PConfig.CloseIdleTime = strconv.Itoa(u)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid close idle timeout(Measured in milliseconds) %v", u)
|
||||
}
|
||||
}
|
||||
|
||||
//SetAccessListType tells the system to treat the AccessList as a whitelist
|
||||
func SetAccessListType(s string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if s == "whitelist" {
|
||||
c.I2PConfig.AccessListType = "whitelist"
|
||||
return nil
|
||||
} else if s == "blacklist" {
|
||||
c.I2PConfig.AccessListType = "blacklist"
|
||||
return nil
|
||||
} else if s == "none" {
|
||||
c.I2PConfig.AccessListType = ""
|
||||
return nil
|
||||
} else if s == "" {
|
||||
c.I2PConfig.AccessListType = ""
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid Access list type(whitelist, blacklist, none)")
|
||||
}
|
||||
}
|
||||
|
||||
//SetAccessList tells the system to treat the AccessList as a whitelist
|
||||
func SetAccessList(s []string) func(*SAMEmit) error {
|
||||
return func(c *SAMEmit) error {
|
||||
if len(s) > 0 {
|
||||
for _, a := range s {
|
||||
c.I2PConfig.AccessList = append(c.I2PConfig.AccessList, a)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
98
emit.go
Normal file
98
emit.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type SAMEmit struct {
|
||||
I2PConfig
|
||||
}
|
||||
|
||||
func (e *SAMEmit) OptStr() string {
|
||||
optStr := ""
|
||||
for _, opt := range e.I2PConfig.Print() {
|
||||
optStr += opt + " "
|
||||
}
|
||||
return optStr
|
||||
}
|
||||
|
||||
func (e *SAMEmit) Hello() string {
|
||||
return fmt.Sprintf("HELLO VERSION MIN=%s MAX=%s \n", e.I2PConfig.MinSAM(), e.I2PConfig.MaxSAM())
|
||||
}
|
||||
|
||||
func (e *SAMEmit) HelloBytes() []byte {
|
||||
return []byte(e.Hello())
|
||||
}
|
||||
|
||||
func (e *SAMEmit) GenerateDestination() string {
|
||||
return fmt.Sprintf("DEST GENERATE %s \n", e.I2PConfig.SignatureType())
|
||||
}
|
||||
|
||||
func (e *SAMEmit) GenerateDestinationBytes() []byte {
|
||||
return []byte(e.GenerateDestination())
|
||||
}
|
||||
|
||||
func (e *SAMEmit) Lookup(name string) string {
|
||||
return fmt.Sprintf("NAMING LOOKUP NAME=%s \n", name)
|
||||
}
|
||||
|
||||
func (e *SAMEmit) LookupBytes(name string) []byte {
|
||||
return []byte(e.Lookup(name))
|
||||
}
|
||||
|
||||
func (e *SAMEmit) Create() string {
|
||||
return fmt.Sprintf(
|
||||
// //1 2 3 4 5 6 7
|
||||
"SESSION CREATE %s%s%s%s%s%s%s \n",
|
||||
e.I2PConfig.SessionStyle(), //1
|
||||
e.I2PConfig.FromPort(), //2
|
||||
e.I2PConfig.ToPort(), //3
|
||||
e.I2PConfig.ID(), //4
|
||||
e.I2PConfig.DestinationKey(), // 5
|
||||
e.I2PConfig.SignatureType(), // 6
|
||||
e.OptStr(), // 7
|
||||
)
|
||||
}
|
||||
|
||||
func (e *SAMEmit) CreateBytes() []byte {
|
||||
log.Println("sam command: " + e.Create())
|
||||
return []byte(e.Create())
|
||||
}
|
||||
|
||||
func (e *SAMEmit) Connect(dest string) string {
|
||||
return fmt.Sprintf(
|
||||
"STREAM CONNECT ID=%s %s %s DESTINATION=%s \n",
|
||||
e.I2PConfig.ID(),
|
||||
e.I2PConfig.FromPort(),
|
||||
e.I2PConfig.ToPort(),
|
||||
dest,
|
||||
)
|
||||
}
|
||||
|
||||
func (e *SAMEmit) ConnectBytes(dest string) []byte {
|
||||
return []byte(e.Connect(dest))
|
||||
}
|
||||
|
||||
func (e *SAMEmit) Accept() string {
|
||||
return fmt.Sprintf(
|
||||
"STREAM ACCEPT ID=%s %s %s",
|
||||
e.I2PConfig.ID(),
|
||||
e.I2PConfig.FromPort(),
|
||||
e.I2PConfig.ToPort(),
|
||||
)
|
||||
}
|
||||
|
||||
func (e *SAMEmit) AcceptBytes() []byte {
|
||||
return []byte(e.Accept())
|
||||
}
|
||||
|
||||
func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
|
||||
var emit SAMEmit
|
||||
for _, o := range opts {
|
||||
if err := o(&emit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &emit, nil
|
||||
}
|
@@ -1,45 +1,103 @@
|
||||
package sam3
|
||||
package i2pkeys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"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}
|
||||
}
|
||||
|
||||
// fileExists checks if a file exists and is not a directory before we
|
||||
// try using it to prevent further errors.
|
||||
func fileExists(filename string) (bool, error) {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !info.IsDir(), nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// load keys from non-standard format by specifying a text file.
|
||||
// If the file does not exist, generate keys, otherwise, fail
|
||||
// closed.
|
||||
func LoadKeys(r string) (I2PKeys, error) {
|
||||
exists, err := fileExists(r)
|
||||
if err != nil {
|
||||
return I2PKeys{}, err
|
||||
}
|
||||
if exists {
|
||||
fi, err := os.Open(r)
|
||||
if err != nil {
|
||||
return I2PKeys{}, err
|
||||
}
|
||||
defer fi.Close()
|
||||
return LoadKeysIncompat(fi)
|
||||
}
|
||||
return I2PKeys{}, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func StoreKeys(k I2PKeys, r string) error {
|
||||
fi, err := os.Open(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
return StoreKeysIncompat(k, fi)
|
||||
}
|
||||
|
||||
// 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 +106,43 @@ 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(base32 version)
|
||||
func (h I2PDestHash) String() string {
|
||||
b32addr := make([]byte, 56)
|
||||
i2pB32enc.Encode(b32addr, h[:])
|
||||
return string(b32addr[:52]) + ".b32.i2p"
|
||||
}
|
||||
|
||||
// get base64 representation of i2p dest sha256 hash(the 44-character one)
|
||||
func (h I2PDestHash) Hash() string {
|
||||
hash := sha256.New()
|
||||
hash.Write(h[:])
|
||||
digest := hash.Sum(nil)
|
||||
buf := make([]byte, 44)
|
||||
i2pB64enc.Encode(buf, digest)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// 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 +161,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 +180,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 {
|
||||
@@ -89,27 +201,31 @@ func NewI2PAddrFromBytes(addr []byte) (I2PAddr, error) {
|
||||
|
||||
// Turns an I2P address to a byte array. The inverse of NewI2PAddrFromBytes().
|
||||
func (addr I2PAddr) ToBytes() ([]byte, error) {
|
||||
buf := make([]byte, i2pB64enc.DecodedLen(len(addr)))
|
||||
if _, err := i2pB64enc.Decode(buf, []byte(addr)); err != nil {
|
||||
return buf, errors.New("Address is not base64-encoded")
|
||||
}
|
||||
return buf, nil
|
||||
return i2pB64enc.DecodeString(string(addr))
|
||||
}
|
||||
|
||||
// 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
|
36
raw.go
36
raw.go
@@ -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
|
||||
}
|
||||
@@ -59,10 +61,10 @@ func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort i
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RawSession{s.address, id, conn, udpconn, keys, rUDPAddr}, nil
|
||||
return &RawSession{s.Config.I2PConfig.Sam(), 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)
|
||||
}
|
||||
|
||||
|
||||
|
72
resolver.go
Normal file
72
resolver.go
Normal file
@@ -0,0 +1,72 @@
|
||||
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 + "\r\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()
|
||||
//log.Println("SAM3", 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)
|
||||
}
|
206
sam3.go
206
sam3.go
@@ -4,17 +4,27 @@ package sam3
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"net"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
)
|
||||
|
||||
|
||||
import (
|
||||
. "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
|
||||
Config SAMEmit
|
||||
keys *i2pkeys.I2PKeys
|
||||
sigType int
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -25,116 +35,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(s.Config.HelloBytes()); 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.Config.I2PConfig.SetSAMAddress(address)
|
||||
s.conn = conn
|
||||
//s.Config.I2PConfig.DestinationKeys = 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.Config.I2PConfig.DestinationKeys
|
||||
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.Config.I2PConfig.DestinationKeys = 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.Config.I2PConfig.DestinationKeys = keys
|
||||
}
|
||||
} else {
|
||||
// persistant
|
||||
_, err = os.Stat(fname)
|
||||
if os.IsNotExist(err) {
|
||||
// make the keys
|
||||
keys, err = sam.NewKeys()
|
||||
if err == nil {
|
||||
sam.Config.I2PConfig.DestinationKeys = 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.Config.I2PConfig.DestinationKeys = 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 NewKeys(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 +227,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 +251,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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
52
sam3_test.go
52
sam3_test.go
@@ -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.String())[: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())
|
||||
}
|
||||
|
||||
|
||||
|
390
stream.go
390
stream.go
@@ -3,198 +3,342 @@ package sam3
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"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.Config.I2PConfig.Sam(), 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.Config.I2PConfig.Sam(), 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.Config.I2PConfig.Sam(), 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.Addr(), 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
|
||||
func (l *StreamListener) From() string {
|
||||
return l.session.from
|
||||
}
|
||||
|
||||
// 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
|
||||
func (l *StreamListener) To() string {
|
||||
return l.session.to
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func ExtractPairString(input, value string) string {
|
||||
parts := strings.Split(input, " ")
|
||||
for _, part := range parts {
|
||||
if strings.HasPrefix(part, value) {
|
||||
kv := strings.SplitN(input, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
return kv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ExtractPairInt(input, value string) int {
|
||||
rv, err := strconv.Atoi(ExtractPairString(input, value))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func ExtractDest(input string) string {
|
||||
return strings.Split(input, " ")[0]
|
||||
}
|
||||
|
||||
// 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)
|
||||
log.Println(line)
|
||||
if err == nil {
|
||||
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
|
||||
// we gud read destination line
|
||||
destline, err := rd.ReadString(10)
|
||||
log.Println(destline)
|
||||
if err == nil {
|
||||
dest := ExtractDest(destline)
|
||||
l.session.from = ExtractPairString(destline, "FROM_PORT")
|
||||
l.session.to = ExtractPairString(destline, "TO_PORT")
|
||||
// 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
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
// +build nettest
|
||||
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
)
|
||||
|
||||
func Test_StreamingDial(t *testing.T) {
|
||||
if testing.Short() {
|
||||
@@ -14,7 +18,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
|
||||
}
|
||||
@@ -26,21 +30,21 @@ func Test_StreamingDial(t *testing.T) {
|
||||
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"})
|
||||
ss, err := sam.NewStreamSession("streamTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Fail()
|
||||
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(", forumAddr.Base32(), forumAddr.DestHash().Hash(), ")")
|
||||
conn, err := ss.DialI2P(forumAddr)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
@@ -52,14 +56,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 +71,7 @@ func Test_StreamingServerClient(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
fmt.Println("Test_StreamingServerClient")
|
||||
sam, err := NewSAM(yoursam)
|
||||
if err != nil {
|
||||
@@ -86,7 +90,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 +144,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
|
||||
// Creates a new StreamingSession, dials to idk.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())
|
||||
@@ -169,12 +172,12 @@ func ExampleStreamSession() {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
someone, err := sam.Lookup("zzz.i2p")
|
||||
someone, err := sam.Lookup("idk.i2p")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
conn, err := ss.DialI2P(someone)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
@@ -184,27 +187,29 @@ 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)
|
||||
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)
|
||||
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
|
||||
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
|
||||
} else {
|
||||
fmt.Println("Read HTTP/HTML from zzz.i2p")
|
||||
fmt.Println("Read HTTP/HTML from idk.i2p")
|
||||
log.Println("Read HTTP/HTML from idk.i2p")
|
||||
}
|
||||
return
|
||||
|
||||
|
||||
// Output:
|
||||
//Sending HTTP GET /
|
||||
//Read HTTP/HTML from zzz.i2p
|
||||
//Read HTTP/HTML from idk.i2p
|
||||
}
|
||||
|
||||
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 +218,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 +227,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 +260,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 +278,9 @@ func ExampleStreamListener() {
|
||||
return
|
||||
}
|
||||
conn.Write([]byte("Hello world!"))
|
||||
|
||||
|
||||
<-quit // waits for client to die, for example only
|
||||
|
||||
|
||||
// Output:
|
||||
//Hello world!
|
||||
}
|
||||
|
@@ -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"}
|
||||
)
|
||||
|
Reference in New Issue
Block a user