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"
2024-09-10 19:21:06 -04:00
"fmt"
2016-02-10 17:54:17 -05:00
"io"
2021-02-21 00:28:26 -05:00
"math/rand"
2016-02-10 17:54:17 -05:00
"net"
"os"
"strings"
2019-05-25 14:36:16 -04:00
2024-11-22 18:48:09 -05:00
"github.com/sirupsen/logrus"
2024-11-09 11:54:54 -05:00
"github.com/go-i2p/i2pkeys"
2024-11-30 18:34:01 -05:00
"github.com/go-i2p/sam3/common"
2024-12-01 10:10:58 -05:00
logger "github.com/go-i2p/sam3/log"
2019-04-09 14:26:27 -04:00
)
2024-10-16 11:23:49 -04:00
func init ( ) {
2024-12-01 10:10:58 -05:00
logger . InitializeSAM3Logger ( )
2024-10-16 11:23:49 -04:00
}
2015-10-15 17:21:11 -04:00
// Used for controlling I2Ps SAMv3.
2024-11-22 20:35:39 -05:00
// This implements the "Control Socket" for all connections.
2015-10-15 17:21:11 -04:00
type SAM struct {
2024-11-30 18:34:01 -05:00
address string
conn net . Conn
keys * i2pkeys . I2PKeys
sigType int
formatter * common . SAMFormatter
version common . Version
2024-11-23 14:19:02 -05:00
SAMEmit
* SAMResolver
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 (
2023-01-16 04:17:51 +00:00
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
2021-02-21 00:28:26 -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 ) ) ]
}
2024-10-15 23:01:00 -04:00
log . WithField ( "randomString" , string ( b ) ) . Debug ( "Generated random string" )
2021-02-21 00:28:26 -05:00
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 ) {
2024-10-15 23:01:00 -04:00
log . WithField ( "address" , address ) . Debug ( "Creating new SAM instance" )
2024-11-30 18:34:01 -05:00
s := SAM {
address : address ,
version : common . SAM31Version ,
formatter : common . NewSAMFormatter ( common . SAM31Version . String ) ,
}
2016-02-10 17:54:17 -05:00
// TODO: clean this up
conn , err := net . Dial ( "tcp" , address )
if err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to dial SAM address" )
2024-09-10 19:21:06 -04:00
return nil , fmt . Errorf ( "error dialing to address '%s': %w" , address , err )
2016-02-10 17:54:17 -05:00
}
2024-11-23 14:19:02 -05:00
if _ , err := conn . Write ( s . SAMEmit . HelloBytes ( ) ) ; err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to write hello message" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-09-10 19:21:06 -04:00
return nil , fmt . Errorf ( "error writing to address '%s': %w" , address , err )
2016-02-10 17:54:17 -05:00
}
2024-11-23 14:19:02 -05:00
/ * buf := make ( [ ] byte , 256 )
n , err := conn . Read ( buf ) * /
reader := bufio . NewReader ( conn )
response , err := reader . ReadString ( '\n' )
2016-02-10 17:54:17 -05:00
if err != nil {
conn . Close ( )
2024-11-23 14:19:02 -05:00
return nil , fmt . Errorf ( "error reading SAM response: %w" , err )
2016-02-10 17:54:17 -05:00
}
2024-11-23 14:19:02 -05:00
buf := [ ] byte ( response )
n := len ( buf )
2019-02-13 23:58:24 -05:00
if strings . Contains ( string ( buf [ : n ] ) , "HELLO REPLY RESULT=OK" ) {
2024-10-15 23:01:00 -04:00
log . Debug ( "SAM hello successful" )
2024-11-23 14:19:02 -05:00
s . SAMEmit . I2PConfig . SetSAMAddress ( address )
2018-12-18 16:59:13 -05:00
s . conn = conn
2024-11-22 18:48:09 -05:00
// s.Config.I2PConfig.DestinationKeys = nil
2024-11-23 14:19:02 -05:00
s . SAMResolver , err = NewSAMResolver ( & s )
2018-12-18 16:59:13 -05:00
if err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to create SAM resolver" )
2024-09-10 19:21:06 -04:00
return nil , fmt . Errorf ( "error creating resolver: %w" , err )
2018-12-18 16:59:13 -05:00
}
return & s , nil
2016-02-10 17:54:17 -05:00
} else if string ( buf [ : n ] ) == "HELLO REPLY RESULT=NOVERSION\n" {
2024-10-15 23:01:00 -04:00
log . Error ( "SAM bridge does not support SAMv3" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "That SAM bridge does not support SAMv3." )
2016-02-10 17:54:17 -05:00
} else {
2024-10-15 23:01:00 -04:00
log . WithField ( "response" , string ( buf [ : n ] ) ) . Error ( "Unexpected SAM response" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( string ( buf [ : n ] ) )
2016-02-10 17:54:17 -05:00
}
2015-10-15 17:21:11 -04:00
}
2019-05-25 14:36:16 -04:00
func ( sam * SAM ) Keys ( ) ( k * i2pkeys . I2PKeys ) {
2024-11-22 18:48:09 -05:00
// TODO: copy them?
2024-10-15 23:01:00 -04:00
log . Debug ( "Retrieving SAM keys" )
2024-11-23 14:19:02 -05:00
k = & sam . SAMEmit . I2PConfig . DestinationKeys
2016-02-10 17:54:17 -05:00
return
2016-01-15 08:49:14 -05:00
}
// read public/private keys from an io.Reader
func ( sam * SAM ) ReadKeys ( r io . Reader ) ( err error ) {
2024-10-15 23:01:00 -04:00
log . Debug ( "Reading keys from io.Reader" )
2019-05-25 14:36:16 -04:00
var keys i2pkeys . I2PKeys
keys , err = i2pkeys . LoadKeysIncompat ( r )
2016-02-10 17:54:17 -05:00
if err == nil {
2024-10-15 23:01:00 -04:00
log . Debug ( "Keys loaded successfully" )
2024-11-23 14:19:02 -05:00
sam . SAMEmit . I2PConfig . DestinationKeys = keys
2016-02-10 17:54:17 -05:00
}
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to load keys" )
2016-02-10 17:54:17 -05:00
return
2016-01-15 08:49:14 -05:00
}
2015-11-30 11:35:25 -05:00
// if keyfile fname does not exist
2019-05-25 14:36:16 -04:00
func ( sam * SAM ) EnsureKeyfile ( fname string ) ( keys i2pkeys . I2PKeys , err error ) {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to load keys" )
2016-02-10 17:54:17 -05:00
if fname == "" {
// transient
keys , err = sam . NewKeys ( )
if err == nil {
2024-11-23 14:19:02 -05:00
sam . SAMEmit . I2PConfig . DestinationKeys = keys
2024-10-15 23:01:00 -04:00
log . WithFields ( logrus . Fields {
"keys" : keys ,
} ) . Debug ( "Generated new transient 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 {
2024-11-23 14:19:02 -05:00
sam . SAMEmit . I2PConfig . DestinationKeys = keys
2016-02-10 17:54:17 -05:00
// save keys
var f io . WriteCloser
2024-11-22 18:48:09 -05:00
f , err = os . OpenFile ( fname , os . O_WRONLY | os . O_CREATE , 0 o600 )
2016-02-10 17:54:17 -05:00
if err == nil {
2019-05-25 14:36:16 -04:00
err = i2pkeys . StoreKeysIncompat ( keys , f )
2016-02-10 17:54:17 -05:00
f . Close ( )
2024-10-15 23:01:00 -04:00
log . Debug ( "Generated and saved new keys" )
2016-02-10 17:54:17 -05:00
}
}
} else if err == nil {
// we haz key file
var f * os . File
f , err = os . Open ( fname )
if err == nil {
2019-05-25 14:36:16 -04:00
keys , err = i2pkeys . LoadKeysIncompat ( f )
2016-02-10 17:54:17 -05:00
if err == nil {
2024-11-23 14:19:02 -05:00
sam . SAMEmit . I2PConfig . DestinationKeys = keys
2024-10-15 23:01:00 -04:00
log . Debug ( "Loaded existing keys from file" )
2016-02-10 17:54:17 -05:00
}
}
}
}
2024-10-15 23:01:00 -04:00
if err != nil {
log . WithError ( err ) . Error ( "Failed to ensure keyfile" )
}
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.
2024-11-22 20:35:39 -05:00
// Add constant for recommended sig type
const (
DEFAULT_SIG_TYPE = "SIGNATURE_TYPE=7" // EdDSA_SHA512_Ed25519
)
2019-05-25 14:36:16 -04:00
func ( sam * SAM ) NewKeys ( sigType ... string ) ( i2pkeys . I2PKeys , error ) {
2024-10-15 23:01:00 -04:00
log . WithField ( "sigType" , sigType ) . Debug ( "Generating new keys" )
2024-12-08 16:21:20 -05:00
if sigType == nil {
sigType = [ ] string { DEFAULT_SIG_TYPE }
2019-02-13 23:58:24 -05:00
}
2024-12-08 16:21:20 -05:00
scmsg := [ ] byte ( fmt . Sprintf ( "DEST GENERATE %s\n" , sigType [ 0 ] ) )
if _ , err := sam . conn . Write ( scmsg ) ; err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to write DEST GENERATE command" )
2024-09-10 19:55:28 -04:00
return i2pkeys . I2PKeys { } , fmt . Errorf ( "error with writing in SAM: %w" , err )
2016-02-10 17:54:17 -05:00
}
buf := make ( [ ] byte , 8192 )
n , err := sam . conn . Read ( buf )
if err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to read SAM response for key generation" )
2024-09-10 19:55:28 -04:00
return i2pkeys . I2PKeys { } , fmt . Errorf ( "error with reading in SAM: %w" , 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 {
2024-10-15 23:01:00 -04:00
log . Error ( "Failed to parse keys from SAM response" )
2024-12-08 13:34:42 -05:00
return i2pkeys . I2PKeys { } , fmt . Errorf ( "Failed to parse keys." )
2016-02-10 17:54:17 -05:00
}
}
2024-10-15 23:01:00 -04:00
log . Debug ( "Successfully generated new keys" )
2024-11-30 18:34:01 -05:00
return i2pkeys . NewKeys ( i2pkeys . 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.
2019-05-25 14:36:16 -04:00
func ( sam * SAM ) Lookup ( name string ) ( i2pkeys . I2PAddr , error ) {
2024-10-15 23:01:00 -04:00
log . WithField ( "name" , name ) . Debug ( "Looking up address" )
2024-11-23 14:19:02 -05:00
return sam . SAMResolver . 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
2024-11-22 20:35:39 -05:00
func ( sam * SAM ) newGenericSession ( style , id string , keys i2pkeys . I2PKeys , options , extras [ ] string ) ( net . Conn , error ) {
2024-10-15 23:01:00 -04:00
log . WithFields ( logrus . Fields { "style" : style , "id" : id } ) . Debug ( "Creating new generic session" )
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
}
2024-11-22 20:35:39 -05:00
func ( sam * SAM ) newGenericSessionWithSignature ( style , id string , keys i2pkeys . I2PKeys , sigType string , options , extras [ ] string ) ( net . Conn , error ) {
2024-10-15 23:01:00 -04:00
log . WithFields ( logrus . Fields { "style" : style , "id" : id , "sigType" : sigType } ) . Debug ( "Creating new generic session with signature" )
2019-03-26 23:04:25 -04:00
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
2024-11-22 20:35:39 -05:00
func ( sam * SAM ) newGenericSessionWithSignatureAndPorts ( style , id , from , to string , keys i2pkeys . I2PKeys , sigType string , options , extras [ ] string ) ( net . Conn , error ) {
2024-10-15 23:01:00 -04:00
log . WithFields ( logrus . Fields { "style" : style , "id" : id , "from" : from , "to" : to , "sigType" : sigType } ) . Debug ( "Creating new generic session with signature and ports" )
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
2024-12-08 13:34:42 -05:00
scmsg := [ ] byte ( fmt . Sprintf ( "SESSION CREATE STYLE=%s FROM_PORT=%s TO_PORT=%s ID=%s DESTINATION=%s %s %s\n" , style , from , to , id , keys . String ( ) , optStr , strings . Join ( extras , " " ) ) )
2024-12-08 16:21:20 -05:00
if style == "PRIMARY" || style == "MASTER" {
scmsg = [ ] byte ( fmt . Sprintf ( "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s %s %s\n" , style , id , keys . String ( ) , optStr , strings . Join ( extras , " " ) ) )
}
2024-10-15 23:01:00 -04:00
2024-12-08 16:21:20 -05:00
log . WithField ( "message" , string ( scmsg ) ) . Debug ( "Sending SESSION CREATE message" , string ( scmsg ) )
2024-10-15 23:01:00 -04:00
2016-02-10 17:54:17 -05:00
for m , i := 0 , 0 ; m != len ( scmsg ) ; i ++ {
if i == 15 {
2024-10-15 23:01:00 -04:00
log . Error ( "Failed to write SESSION CREATE message after 15 attempts" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "writing to SAM failed" )
2016-02-10 17:54:17 -05:00
}
n , err := conn . Write ( scmsg [ m : ] )
if err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to write to SAM connection" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-09-10 19:55:28 -04:00
return nil , fmt . Errorf ( "writing to connection failed: %w" , err )
2016-02-10 17:54:17 -05:00
}
m += n
}
buf := make ( [ ] byte , 4096 )
n , err := conn . Read ( buf )
if err != nil {
2024-10-15 23:01:00 -04:00
log . WithError ( err ) . Error ( "Failed to read SAM response" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-09-10 19:55:28 -04:00
return nil , fmt . Errorf ( "reading from connection failed: %w" , err )
2016-02-10 17:54:17 -05:00
}
text := string ( buf [ : n ] )
2024-10-15 23:01:00 -04:00
log . WithField ( "response" , text ) . Debug ( "Received SAM response" )
2016-02-10 17:54:17 -05:00
if strings . HasPrefix ( text , session_OK ) {
if keys . String ( ) != text [ len ( session_OK ) : len ( text ) - 1 ] {
2024-10-15 23:01:00 -04:00
log . Error ( "SAM created a tunnel with different keys than requested" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "SAMv3 created a tunnel with keys other than the ones we asked it for" )
2016-02-10 17:54:17 -05:00
}
2024-10-16 10:02:47 -04:00
log . Debug ( "Successfully created new session" )
2016-02-10 17:54:17 -05:00
return conn , nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == session_DUPLICATE_ID {
2024-10-16 10:02:47 -04:00
log . Error ( "Duplicate tunnel name" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "Duplicate tunnel name" )
2016-02-10 17:54:17 -05:00
} else if text == session_DUPLICATE_DEST {
2024-10-16 10:02:47 -04:00
log . Error ( "Duplicate destination" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "Duplicate destination" )
2016-02-10 17:54:17 -05:00
} else if text == session_INVALID_KEY {
2024-10-16 10:02:47 -04:00
log . Error ( "Invalid key for SAM session" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:34:42 -05:00
return nil , fmt . Errorf ( "Invalid key - SAM session" )
2016-02-10 17:54:17 -05:00
} else if strings . HasPrefix ( text , session_I2P_ERROR ) {
2024-10-16 10:02:47 -04:00
log . WithField ( "error" , text [ len ( session_I2P_ERROR ) : ] ) . Error ( "I2P error" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:49:52 -05:00
return nil , fmt . Errorf ( "I2P error %s" , text [ len ( session_I2P_ERROR ) : ] )
2016-02-10 17:54:17 -05:00
} else {
2024-10-16 10:02:47 -04:00
log . WithField ( "reply" , text ) . Error ( "Unable to parse SAMv3 reply" )
2016-02-10 17:54:17 -05:00
conn . Close ( )
2024-12-08 13:49:52 -05:00
return nil , fmt . Errorf ( "Unable to parse SAMv3 reply: %s" , text )
2016-02-10 17:54:17 -05:00
}
2015-10-15 17:21:11 -04:00
}
2024-11-22 20:35:39 -05:00
// Close this sam session
2015-10-15 17:21:11 -04:00
func ( sam * SAM ) Close ( ) error {
2024-10-16 10:02:47 -04:00
log . Debug ( "Closing SAM session" )
2016-02-10 17:54:17 -05:00
return sam . conn . Close ( )
2015-10-15 17:21:11 -04:00
}
2024-11-22 20:35:39 -05:00
// CloseNotify the socket with a QUIT message
func ( sam * SAM ) CloseNotify ( ) error {
log . Debug ( "Quitting SAM session" )
_ , err := sam . conn . Write ( [ ] byte ( "QUIT\n" ) )
if err != nil {
return fmt . Errorf ( "close notification failed: %v" , err )
}
return nil
}