21 Commits

Author SHA1 Message Date
idk
a12f9ba498 add makefile to ease releases 2019-10-22 02:26:57 -04:00
idk
4a24538cd5 windows compatibility fixes 2019-10-22 02:24:38 -04:00
idk
f617388473 windows compatibility fixes 2019-10-22 02:19:01 -04:00
idk
c2b2b39c74 gofmt 2019-10-22 02:06:08 -04:00
idk
0067d37ca3 windows compatibility fixes 2019-10-22 02:05:58 -04:00
idk
f8d54526ea update go mod 2019-07-30 14:51:40 -04:00
idk
e9868b791e enable setting the write buffer size 2019-07-30 14:50:14 -04:00
idk
a810f009bd update modules 2019-07-29 20:02:43 -04:00
idk
99ad6522eb implement UDPConn as well 2019-06-12 23:41:17 -04:00
idk
6f6be01fb2 implement UDPConn too 2019-06-12 23:33:06 -04:00
idk
7438f855cd implement UDPConn too 2019-06-12 23:32:35 -04:00
idk
eabe42f33f completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:22 -04:00
idk
0e9665027e completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:16 -04:00
idk
79b526d69c test, fmt, update changelog and version number 2019-05-18 18:34:57 -04:00
idk
5b1dcc54aa Create stable branch 2019-05-18 18:32:21 -04:00
idk
6b863d7514 Create stable branch 2019-05-18 18:32:12 -04:00
idk
9ee74a89d3 Create stable branch 2019-05-18 18:31:44 -04:00
idk
6029d7dd6e Create stable branch 2019-05-18 18:30:22 -04:00
idk
265e8b21b4 update changelog 2019-05-18 18:21:01 -04:00
idk
9d24d56c28 thought that might have happened 2019-04-29 16:21:40 -04:00
idk
d622765b79 forgot to add moved i2pkeys dir 2019-04-20 14:50:55 -04:00
17 changed files with 401 additions and 100 deletions

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
USER_GH=eyedeekay
VERSION=0.0.2
version:
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r sam3 -t v$(VERSION) 2> /dev/null; true
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r sam3 -t v$(VERSION) -d "version $(VERSION)"

View File

@ -78,7 +78,7 @@ 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 ##
@ -88,4 +88,4 @@ Public domain.
## Author ##
* Kalle Vedin `kalle.vedin@fripost.org`
* Unknown Name (majestrate)
* Unknown Name (majestrate)

View File

@ -1,18 +1,20 @@
package sam3
import (
"github.com/eyedeekay/sam3/i2pkeys"
"net"
"time"
)
/*
import (
. "github.com/eyedeekay/sam3/i2pkeys"
)
*/
// Implements net.Conn
type SAMConn struct {
laddr I2PAddr
raddr I2PAddr
laddr i2pkeys.I2PAddr
raddr i2pkeys.I2PAddr
conn net.Conn
}
@ -38,7 +40,7 @@ func (sc *SAMConn) LocalAddr() net.Addr {
}
// Implements net.Conn
func (sc *SAMConn) localAddr() I2PAddr {
func (sc *SAMConn) localAddr() i2pkeys.I2PAddr {
return sc.laddr
}
@ -47,7 +49,7 @@ func (sc *SAMConn) RemoteAddr() net.Addr {
}
// Implements net.Conn
func (sc *SAMConn) remoteAddr() I2PAddr {
func (sc *SAMConn) remoteAddr() i2pkeys.I2PAddr {
return sc.raddr
}

View File

@ -4,10 +4,8 @@ import (
"fmt"
"net"
"strconv"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// sam config
@ -38,7 +36,7 @@ func (cfg *Config) StreamSession() (session *StreamSession, err error) {
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys I2PKeys
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// create session
@ -55,7 +53,7 @@ func (cfg *Config) DatagramSession() (session *DatagramSession, err error) {
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys I2PKeys
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// determine udp port

View File

@ -6,10 +6,8 @@ import (
"net"
"strconv"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
@ -17,17 +15,18 @@ import (
// 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,
// 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")
}
@ -61,13 +60,17 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
if err != nil {
return nil, err
}
return &DatagramSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, keys, rUDPAddr}, nil
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil}, nil
}
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.
// implements net.PacketConn
@ -80,7 +83,7 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
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
@ -89,11 +92,11 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, 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) {
@ -105,16 +108,25 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
}
}
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 net.Addr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
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()
@ -126,7 +138,7 @@ func (s *DatagramSession) Close() error {
}
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() I2PAddr {
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
}
@ -161,3 +173,7 @@ func (s *DatagramSession) SetReadDeadline(t time.Time) error {
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)
}

View File

@ -4,6 +4,7 @@ package sam3
import (
"fmt"
"log"
"testing"
"time"
)
@ -124,6 +125,58 @@ func ExampleDatagramSession() {
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!
}
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

27
debian/changelog vendored
View File

@ -1,3 +1,30 @@
<<<<<<< HEAD
golang-github-eyedeekay-sam3 (0.3.2.5) bionic; urgency=medium
[ idk ]
* Updates to SAMv3.2
* Move i2pkeys to own directory/library
* Bug fixes
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 21:40:35 -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
>>>>>>> stable
golang-github-eyedeekay-sam3 (0.0~git20190223.af5a3f3) bionic; urgency=medium
[ idk ]

3
debian/control vendored
View File

@ -15,7 +15,8 @@ Testsuite: autopkgtest-pkg-go
Package: golang-github-eyedeekay-sam3-dev
Architecture: all
Depends: ${misc:Depends}, (i2p | i2pd)
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

1
debian/files vendored Normal file
View File

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

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/eyedeekay/sam3
go 1.12
require github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab h1:EfTRHxGSbiaEyxNzvKRBWVIDw3mD8xXGxj4gvwFzY7Q=
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab/go.mod h1:h7mvUAMgZ/rtRDUOkvKTK+8LnDMeUhJSoa5EPdB51fc=

188
i2pkeys/I2PAddr.go Normal file
View File

@ -0,0 +1,188 @@
package i2pkeys
import (
"bytes"
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"errors"
"io"
"strings"
)
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
// The public and private keys associated with an I2P destination. I2P hides the
// details of exactly what this is, so treat them as blobs, but generally: One
// pair of DSA keys, one pair of ElGamal keys, and sometimes (almost never) also
// a certificate. String() returns you the full content of I2PKeys and Addr()
// returns the public keys.
type I2PKeys struct {
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
// generated by String().)
func NewKeys(addr I2PAddr, both string) I2PKeys {
return I2PKeys{addr, both}
}
// load keys from non standard format
func LoadKeysIncompat(r io.Reader) (k I2PKeys, err error) {
var buff bytes.Buffer
_, err = io.Copy(&buff, r)
if err == nil {
parts := strings.Split(buff.String(), "\n")
k = I2PKeys{I2PAddr(parts[0]), parts[1]}
}
return
}
// store keys in non standard format
func StoreKeysIncompat(k I2PKeys, w io.Writer) (err error) {
_, err = io.WriteString(w, k.Address.Base64()+"\n"+k.Both)
return
}
// Returns the public keys of the I2PKeys.
func (k I2PKeys) Addr() I2PAddr {
return k.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
}
// I2PAddr represents an I2P destination, almost equivalent to an IP address.
// This is the humongously huge base64 representation of such an address, which
// really is just a pair of public keys and also maybe a certificate. (I2P hides
// the details of exactly what it is. Read the I2P specifications for more info.)
type I2PAddr string
// an i2p destination hash, the .b32.i2p address if you will
type I2PDestHash [32]byte
// create a desthash from a string b32.i2p address
func DestHashFromString(str string) (dhash I2PDestHash, err error) {
if strings.HasSuffix(str, ".b32.i2p") && len(str) == 60 {
// valid
_, err = i2pB32enc.Decode(dhash[:], []byte(str[:52]+"===="))
} else {
// invalid
err = errors.New("invalid desthash format")
}
return
}
// get string representation of i2p dest hash
func (h I2PDestHash) String() string {
b32addr := make([]byte, 56)
i2pB32enc.Encode(b32addr, h[:])
return string(b32addr[:52]) + ".b32.i2p"
}
// Returns "I2P"
func (h *I2PDestHash) Network() string {
return "I2P"
}
// Returns the base64 representation of the I2PAddr
func (a I2PAddr) Base64() string {
return string(a)
}
// Returns the I2P destination (base64-encoded)
func (a I2PAddr) String() string {
return string(a)
}
// Returns "I2P"
func (a I2PAddr) Network() string {
return "I2P"
}
// Creates a new I2P address from a base64-encoded string. 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")
}
buf := make([]byte, i2pB64enc.DecodedLen(len(addr)))
if _, err := i2pB64enc.Decode(buf, []byte(addr)); err != nil {
return I2PAddr(""), errors.New("Address is not base64-encoded")
}
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 {
return I2PAddr(""), errors.New("Not an I2P address")
}
buf := make([]byte, i2pB64enc.EncodedLen(len(addr)))
i2pB64enc.Encode(buf, addr)
return I2PAddr(string(buf)), nil
}
// 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
}
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() (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)
copy(h[:], digest)
return
}
// Makes any string into a *.b32.i2p human-readable I2P address. This makes no
// sense, unless "anything" is an I2P destination of some sort.
func Base32(anything string) string {
return I2PAddr(anything).Base32()
}

22
raw.go
View File

@ -6,10 +6,8 @@ import (
"net"
"strconv"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// The RawSession provides no authentication of senders, and there is no sender
@ -19,17 +17,17 @@ import (
// 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,
// 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")
}
@ -87,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)
@ -105,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()
}

View File

@ -5,10 +5,8 @@ import (
"bytes"
"errors"
"strings"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
type SAMResolver struct {
@ -33,19 +31,19 @@ func NewFullSAMResolver(address string) (*SAMResolver, error) {
// 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) (I2PAddr, error) {
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\n")); err != nil {
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 I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
buf := make([]byte, 4096)
n, err := sam.conn.Read(buf)
if err != nil {
sam.Close()
return I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
if n <= 13 || !strings.HasPrefix(string(buf[:n]), "NAMING REPLY ") {
return I2PAddr(""), errors.New("Failed to parse.")
return i2pkeys.I2PAddr(""), errors.New("Failed to parse.")
}
s := bufio.NewScanner(bytes.NewReader(buf[13:n]))
s.Split(bufio.ScanWords)
@ -53,6 +51,7 @@ func (sam *SAMResolver) Resolve(name string) (I2PAddr, error) {
errStr := ""
for s.Scan() {
text := s.Text()
//log.Println("SAM3", text)
if text == "RESULT=OK" {
continue
} else if text == "RESULT=INVALID_KEY" {
@ -62,12 +61,12 @@ func (sam *SAMResolver) Resolve(name string) (I2PAddr, error) {
} else if text == "NAME="+name {
continue
} else if strings.HasPrefix(text, "VALUE=") {
return I2PAddr(text[6:]), nil
return i2pkeys.I2PAddr(text[6:]), nil
} else if strings.HasPrefix(text, "MESSAGE=") {
errStr += " " + text[8:]
} else {
continue
}
}
return I2PAddr(""), errors.New(errStr)
return i2pkeys.I2PAddr(""), errors.New(errStr)
}

48
sam3.go
View File

@ -9,6 +9,8 @@ import (
"net"
"os"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
)
import (
@ -18,10 +20,12 @@ import (
// Used for controlling I2Ps SAMv3.
type SAM struct {
//address string
address string
conn net.Conn
resolver *SAMResolver
Config SAMEmit
keys *i2pkeys.I2PKeys
sigType int
}
const (
@ -49,7 +53,7 @@ func NewSAM(address string) (*SAM, error) {
if err != nil {
return nil, err
}
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
conn.Close()
return nil, err
}
@ -62,7 +66,7 @@ func NewSAM(address string) (*SAM, error) {
if strings.Contains(string(buf[:n]), "HELLO REPLY RESULT=OK") {
s.Config.I2PConfig.SetSAMAddress(address)
s.conn = conn
s.Config.I2PConfig.DestinationKeys = nil
//s.Config.I2PConfig.DestinationKeys = nil
s.resolver, err = NewSAMResolver(&s)
if err != nil {
return nil, err
@ -78,29 +82,29 @@ func NewSAM(address string) (*SAM, error) {
}
}
func (sam *SAM) Keys() (k *I2PKeys) {
func (sam *SAM) Keys() (k *i2pkeys.I2PKeys) {
//TODO: copy them?
k = sam.Config.I2PConfig.DestinationKeys
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
keys, err = LoadKeysIncompat(r)
var keys i2pkeys.I2PKeys
keys, err = i2pkeys.LoadKeysIncompat(r)
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
}
return
}
// if keyfile fname does not exist
func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
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
sam.Config.I2PConfig.DestinationKeys = keys
}
} else {
// persistant
@ -109,12 +113,12 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
// make the keys
keys, err = sam.NewKeys()
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
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 = StoreKeysIncompat(keys, f)
err = i2pkeys.StoreKeysIncompat(keys, f)
f.Close()
}
}
@ -123,9 +127,9 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
var f *os.File
f, err = os.Open(fname)
if err == nil {
keys, err = LoadKeysIncompat(f)
keys, err = i2pkeys.LoadKeysIncompat(f)
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
}
}
}
@ -136,18 +140,18 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
// Creates the I2P-equivalent of an IP address, that is unique and only the one
// who has the private keys can send messages from. The public keys are the I2P
// desination (the address) that anyone can send messages to.
func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
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{}, err
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)
@ -164,7 +168,7 @@ func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
} 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 NewKeys(I2PAddr(pub), priv), nil
@ -172,7 +176,7 @@ func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
// 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) {
func (sam *SAM) Lookup(name string) (i2pkeys.I2PAddr, error) {
return sam.resolver.Resolve(name)
}
@ -181,11 +185,11 @@ func (sam *SAM) Lookup(name string) (I2PAddr, error) {
// 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, sigType string, options []string, extras []string) (net.Conn, error) {
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)
}
@ -194,7 +198,7 @@ func (sam *SAM) newGenericSessionWithSignature(style, id string, keys I2PKeys, s
// 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, sigType string, options []string, extras []string) (net.Conn, error) {
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 {

View File

@ -11,18 +11,16 @@ import (
"strconv"
"strings"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"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
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
@ -52,18 +50,18 @@ func (ss *StreamSession) Close() error {
}
// 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
// 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
@ -73,7 +71,7 @@ func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*St
// 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, options []string, sigType string) (*StreamSession, error) {
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
@ -83,7 +81,7 @@ func (sam *SAM) NewStreamSessionWithSignature(id string, keys I2PKeys, options [
// 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, options []string, sigType string) (*StreamSession, error) {
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
@ -92,14 +90,14 @@ func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys
}
// lookup name, convienence function
func (s *StreamSession) Lookup(name string) (I2PAddr, error) {
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 I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
// context-aware dialer, eventually...
@ -121,7 +119,7 @@ func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SA
}
}
i2paddr, err := NewI2PAddrFromString(addr)
i2paddr, err := i2pkeys.NewI2PAddrFromString(addr)
if err != nil {
return nil, err
}
@ -163,7 +161,7 @@ func (s *StreamSession) deadline(ctx context.Context, now time.Time) (earliest t
// implement net.Dialer
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
var i2paddr I2PAddr
var i2paddr i2pkeys.I2PAddr
var host string
host, _, err = net.SplitHostPort(addr)
if err == nil {
@ -173,7 +171,7 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
i2paddr, err = s.Lookup(host)
} else {
// probably a destination
i2paddr = I2PAddr(host)
i2paddr = i2pkeys.I2PAddr(host)
}
if err == nil {
return s.DialI2P(i2paddr)
@ -183,7 +181,7 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
}
// 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
@ -248,7 +246,7 @@ type StreamListener struct {
// our session id
id string
// our local address for this sam socket
laddr I2PAddr
laddr i2pkeys.I2PAddr
}
func (l *StreamListener) From() string {
@ -325,7 +323,7 @@ func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: I2PAddr(dest),
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {

View File

@ -4,12 +4,11 @@ package sam3
import (
"fmt"
"log"
"strings"
"testing"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {
@ -31,7 +30,7 @@ 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()
@ -45,7 +44,7 @@ func Test_StreamingDial(t *testing.T) {
t.Fail()
return
}
fmt.Println("\tDialing i2p-projekt.i2p")
fmt.Println("\tDialing i2p-projekt.i2p(", forumAddr.Base32(), ")")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
@ -194,8 +193,10 @@ func ExampleStreamSession() {
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)
log.Printf("Probably failed to StreamSession.DialI2P(zzz.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("Read HTTP/HTML from zzz.i2p")
log.Println("Read HTTP/HTML from zzz.i2p")
}
return
@ -226,7 +227,7 @@ 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())