Files
sam3/sam3.go

271 lines
7.6 KiB
Go
Raw Normal View History

2015-10-15 17:21:11 -04:00
// Library for I2Ps SAMv3 bridge (https://geti2p.com)
package sam3
import (
2016-02-10 17:54:17 -05:00
"bufio"
"bytes"
"errors"
"io"
"math/rand"
2016-02-10 17:54:17 -05:00
"net"
"os"
"strings"
2022-03-10 01:01:31 -05:00
"github.com/eyedeekay/i2pkeys"
2015-10-15 17:21:11 -04:00
2022-03-10 01:01:31 -05:00
. "github.com/eyedeekay/i2pkeys"
)
2015-10-15 17:21:11 -04:00
// Used for controlling I2Ps SAMv3.
type SAM struct {
2018-12-18 16:59:13 -05:00
address string
conn net.Conn
resolver *SAMResolver
2019-04-10 00:47:41 -04:00
Config SAMEmit
keys *i2pkeys.I2PKeys
2019-02-13 23:58:24 -05:00
sigType int
2015-10-15 17:21:11 -04:00
}
const (
2016-02-10 17:54:17 -05:00
session_OK = "SESSION STATUS RESULT=OK DESTINATION="
session_DUPLICATE_ID = "SESSION STATUS RESULT=DUPLICATED_ID\n"
session_DUPLICATE_DEST = "SESSION STATUS RESULT=DUPLICATED_DEST\n"
session_INVALID_KEY = "SESSION STATUS RESULT=INVALID_KEY\n"
session_I2P_ERROR = "SESSION STATUS RESULT=I2P_ERROR MESSAGE="
2015-10-15 17:21:11 -04:00
)
2019-03-26 22:22:00 -04:00
const (
Sig_NONE = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
2019-03-26 23:17:57 -04:00
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"
2019-03-26 22:22:00 -04:00
)
2019-02-13 23:58:24 -05:00
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandString() string {
n := 4
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
2015-10-15 17:21:11 -04:00
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
2018-12-18 16:59:13 -05:00
var s SAM
2016-02-10 17:54:17 -05:00
// TODO: clean this up
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
}
2019-04-29 16:21:40 -04:00
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
2016-02-10 17:54:17 -05:00
conn.Close()
return nil, err
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
2019-02-13 23:58:24 -05:00
if strings.Contains(string(buf[:n]), "HELLO REPLY RESULT=OK") {
2019-04-10 00:47:41 -04:00
s.Config.I2PConfig.SetSAMAddress(address)
2018-12-18 16:59:13 -05:00
s.conn = conn
2019-04-29 16:21:40 -04:00
//s.Config.I2PConfig.DestinationKeys = nil
2018-12-18 16:59:13 -05:00
s.resolver, err = NewSAMResolver(&s)
if err != nil {
return nil, err
}
return &s, nil
//return &SAM{address, conn, nil, nil}, nil
2016-02-10 17:54:17 -05:00
} 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]))
}
2015-10-15 17:21:11 -04:00
}
func (sam *SAM) Keys() (k *i2pkeys.I2PKeys) {
2016-02-10 17:54:17 -05:00
//TODO: copy them?
2019-04-29 16:21:40 -04:00
k = &sam.Config.I2PConfig.DestinationKeys
2016-02-10 17:54:17 -05:00
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)
2016-02-10 17:54:17 -05:00
if err == nil {
2019-04-29 16:21:40 -04:00
sam.Config.I2PConfig.DestinationKeys = keys
2016-02-10 17:54:17 -05:00
}
return
}
2015-11-30 11:35:25 -05:00
// if keyfile fname does not exist
func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
2016-02-10 17:54:17 -05:00
if fname == "" {
// transient
keys, err = sam.NewKeys()
if err == nil {
2019-04-29 16:21:40 -04:00
sam.Config.I2PConfig.DestinationKeys = keys
2016-02-10 17:54:17 -05:00
}
} else {
2024-01-09 13:40:42 -05:00
// persistent
2016-02-10 17:54:17 -05:00
_, err = os.Stat(fname)
if os.IsNotExist(err) {
// make the keys
keys, err = sam.NewKeys()
if err == nil {
2019-04-29 16:21:40 -04:00
sam.Config.I2PConfig.DestinationKeys = keys
2016-02-10 17:54:17 -05:00
// 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)
2016-02-10 17:54:17 -05:00
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)
2016-02-10 17:54:17 -05:00
if err == nil {
2019-04-29 16:21:40 -04:00
sam.Config.I2PConfig.DestinationKeys = keys
2016-02-10 17:54:17 -05:00
}
}
}
}
return
2015-11-30 11:35:25 -05:00
}
2015-10-15 17:21:11 -04:00
// Creates the I2P-equivalent of an IP address, that is unique and only the one
2015-12-14 09:00:11 -05:00
// who has the private keys can send messages from. The public keys are the I2P
2015-10-15 17:21:11 -04:00
// desination (the address) that anyone can send messages to.
func (sam *SAM) NewKeys(sigType ...string) (i2pkeys.I2PKeys, error) {
2019-03-26 22:22:00 -04:00
sigtmp := ""
2019-02-13 23:58:24 -05:00
if len(sigType) > 0 {
sigtmp = sigType[0]
}
2019-10-22 02:19:01 -04:00
if _, err := sam.conn.Write([]byte("DEST GENERATE " + sigtmp + "\n")); err != nil {
return i2pkeys.I2PKeys{}, err
2016-02-10 17:54:17 -05:00
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return i2pkeys.I2PKeys{}, err
2016-02-10 17:54:17 -05:00
}
s := bufio.NewScanner(bytes.NewReader(buf[:n]))
s.Split(bufio.ScanWords)
var pub, priv string
for s.Scan() {
text := s.Text()
if text == "DEST" {
continue
} else if text == "REPLY" {
continue
} else if strings.HasPrefix(text, "PUB=") {
pub = text[4:]
} else if strings.HasPrefix(text, "PRIV=") {
priv = text[5:]
} else {
return i2pkeys.I2PKeys{}, errors.New("Failed to parse keys.")
2016-02-10 17:54:17 -05:00
}
}
return NewKeys(I2PAddr(pub), priv), nil
2015-10-15 17:21:11 -04:00
}
// 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) (i2pkeys.I2PAddr, error) {
2018-12-18 16:59:13 -05:00
return sam.resolver.Resolve(name)
2015-10-15 17:21:11 -04:00
}
2015-12-14 09:00:11 -05:00
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
2015-10-15 17:21:11 -04:00
// 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.I2PKeys, options []string, extras []string) (net.Conn, error) {
2019-03-26 23:19:10 -04:00
return sam.newGenericSessionWithSignature(style, id, keys, Sig_NONE, options, extras)
2019-02-13 23:58:24 -05:00
}
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)
}
2019-02-13 23:58:24 -05:00
// 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) {
2015-10-15 17:21:11 -04:00
2024-01-07 12:09:13 -05:00
optStr := GenerateOptionString(options)
2016-02-10 17:54:17 -05:00
conn := sam.conn
fp := ""
tp := ""
if from != "0" {
fp = " FROM_PORT=" + from
}
if to != "0" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION CREATE STYLE=" + style + fp + tp + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + strings.Join(extras, " ") + "\n")
2016-02-10 17:54:17 -05:00
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
conn.Close()
return nil, err
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
text := string(buf[:n])
if strings.HasPrefix(text, session_OK) {
if keys.String() != text[len(session_OK):len(text)-1] {
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
} else if text == session_DUPLICATE_ID {
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == session_DUPLICATE_DEST {
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
conn.Close()
2022-08-05 01:48:06 -04:00
return nil, errors.New("Invalid key - SAM session")
2016-02-10 17:54:17 -05:00
} else if strings.HasPrefix(text, session_I2P_ERROR) {
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
} else {
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
2015-10-15 17:21:11 -04:00
}
// close this sam session
func (sam *SAM) Close() error {
2016-02-10 17:54:17 -05:00
return sam.conn.Close()
2015-10-15 17:21:11 -04:00
}