Files
goSam/client.go

232 lines
5.1 KiB
Go
Raw Normal View History

package goSam
import (
"bufio"
2019-04-25 23:45:24 -04:00
"crypto/sha256"
"encoding/base32"
"encoding/base64"
2019-04-25 23:45:24 -04:00
"encoding/binary"
"fmt"
"math"
"math/rand"
"net"
2019-04-25 23:45:24 -04:00
"strings"
2015-03-25 21:37:41 +01:00
"github.com/eyedeekay/gosam/debug"
)
// A Client represents a single Connection to the SAM bridge
type Client struct {
2019-04-25 23:45:24 -04:00
host string
port string
fromport string
toport string
SamConn net.Conn
2015-03-25 21:37:41 +01:00
rd *bufio.Reader
sigType string
destination string
2019-02-24 23:08:01 -05:00
inLength uint
inVariance int
inQuantity uint
inBackups uint
outLength uint
outVariance int
outQuantity uint
outBackups uint
dontPublishLease bool
encryptLease bool
reduceIdle bool
reduceIdleTime uint
reduceIdleQuantity uint
closeIdle bool
closeIdleTime uint
2019-02-24 23:08:01 -05:00
compression bool
2019-02-21 17:38:11 -05:00
debug bool
2019-02-27 20:46:18 -05:00
//NEVER, EVER modify lastaddr or id yourself. They are used internally only.
lastaddr string
id int32
}
2019-02-24 23:08:01 -05:00
var SAMsigTypes = []string{
"SIGNATURE_TYPE=DSA_SHA1",
"SIGNATURE_TYPE=ECDSA_SHA256_P256",
"SIGNATURE_TYPE=ECDSA_SHA384_P384",
"SIGNATURE_TYPE=ECDSA_SHA512_P521",
"SIGNATURE_TYPE=EdDSA_SHA512_Ed25519",
}
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
2014-02-14 12:05:13 +01:00
// NewDefaultClient creates a new client, connecting to the default host:port at localhost:7656
func NewDefaultClient() (*Client, error) {
return NewClient("localhost:7656")
}
2014-02-14 12:05:13 +01:00
// NewClient creates a new client, connecting to a specified port
func NewClient(addr string) (*Client, error) {
return NewClientFromOptions(SetAddr(addr))
}
2019-02-27 20:46:18 -05:00
// NewID generates a random number to use as an tunnel name
func (c *Client) NewID() int32 {
return rand.Int31n(math.MaxInt32)
}
// Destination returns the full destination of the local tunnel
func (c *Client) Destination() string {
return c.destination
}
// Base32 returns the base32 of the local tunnel
func (c *Client) Base32() string {
2019-04-25 23:45:24 -04:00
hash := sha256.New()
2019-05-16 18:56:59 -04:00
b64, err := i2pB64enc.DecodeString(c.Base64())
2019-05-16 18:56:33 -04:00
if err != nil {
return ""
}
2019-05-16 18:56:59 -04:00
hash.Write([]byte(b64))
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(hash.Sum(nil)), "=", "", -1))
}
func (c *Client) base64() []byte {
if c.destination != "" {
s, _ := i2pB64enc.DecodeString(c.destination)
alen := binary.BigEndian.Uint16(s[385:387])
return s[:387+alen]
}
return []byte("")
}
// Base64 returns the base64 of the local tunnel
func (c *Client) Base64() string {
return i2pB64enc.EncodeToString(c.base64())
}
// NewClientFromOptions creates a new client, connecting to a specified port
func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
var c Client
c.host = "127.0.0.1"
c.port = "7656"
c.inLength = 3
c.inVariance = 0
c.inQuantity = 1
c.inBackups = 1
c.outLength = 3
c.outVariance = 0
c.outQuantity = 1
c.outBackups = 1
c.dontPublishLease = true
c.encryptLease = false
c.reduceIdle = false
c.reduceIdleTime = 300000
c.reduceIdleQuantity = 1
c.closeIdle = true
c.closeIdleTime = 600000
c.debug = false
c.sigType = SAMsigTypes[4]
c.id = 0
2019-02-27 22:00:35 -05:00
c.lastaddr = "invalid"
2019-03-14 22:46:17 -04:00
c.destination = ""
for _, o := range opts {
if err := o(&c); err != nil {
return nil, err
}
}
conn, err := net.Dial("tcp", c.samaddr())
if err != nil {
return nil, err
}
if c.debug {
2015-03-25 21:37:41 +01:00
conn = debug.WrapConn(conn)
}
c.SamConn = conn
c.rd = bufio.NewReader(conn)
return &c, c.hello()
}
2019-04-25 23:45:24 -04:00
func (p *Client) ID() string {
return fmt.Sprintf("%d", p.id)
}
//return the combined host:port of the SAM bridge
func (c *Client) samaddr() string {
return fmt.Sprintf("%s:%s", c.host, c.port)
}
2014-02-11 13:10:24 +01:00
// send the initial handshake command and check that the reply is ok
2015-03-25 22:03:05 +01:00
func (c *Client) hello() error {
2019-03-14 22:46:17 -04:00
r, err := c.sendCmd("HELLO VERSION MIN=3.0 MAX=3.2\n")
2014-02-11 13:10:24 +01:00
if err != nil {
return err
}
2014-02-11 13:10:24 +01:00
if r.Topic != "HELLO" {
return fmt.Errorf("Unknown Reply: %+v\n", r)
}
if r.Pairs["RESULT"] != "OK" {
2014-02-11 13:10:24 +01:00
return fmt.Errorf("Handshake did not succeed\nReply:%+v\n", r)
}
2014-02-11 13:10:24 +01:00
return nil
}
// helper to send one command and parse the reply by sam
2015-03-25 22:03:05 +01:00
func (c *Client) sendCmd(str string, args ...interface{}) (*Reply, error) {
if _, err := fmt.Fprintf(c.SamConn, str, args...); err != nil {
return nil, err
}
2015-03-25 21:37:41 +01:00
line, err := c.rd.ReadString('\n')
if err != nil {
2015-03-25 22:03:05 +01:00
return nil, err
}
2014-02-11 13:10:24 +01:00
2015-03-25 21:37:41 +01:00
return parseReply(line)
}
// Close the underlying socket to SAM
func (c *Client) Close() error {
2015-03-25 22:03:05 +01:00
c.rd = nil
return c.SamConn.Close()
}
2019-02-27 20:46:18 -05:00
// NewClient generates an exact copy of the client with the same options
func (c *Client) NewClient() (*Client, error) {
return NewClientFromOptions(
SetHost(c.host),
SetPort(c.port),
SetDebug(c.debug),
SetInLength(c.inLength),
SetOutLength(c.outLength),
SetInVariance(c.inVariance),
SetOutVariance(c.outVariance),
SetInQuantity(c.inQuantity),
SetOutQuantity(c.outQuantity),
SetInBackups(c.inBackups),
SetOutBackups(c.outBackups),
SetUnpublished(c.dontPublishLease),
SetEncrypt(c.encryptLease),
SetReduceIdle(c.reduceIdle),
SetReduceIdleTime(c.reduceIdleTime),
SetReduceIdleQuantity(c.reduceIdleQuantity),
SetCloseIdle(c.closeIdle),
SetCloseIdleTime(c.closeIdleTime),
SetCompression(c.compression),
setlastaddr(c.lastaddr),
setid(c.id),
)
}