2015-10-15 17:21:11 -04:00
|
|
|
package sam3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2018-11-27 12:10:38 -05:00
|
|
|
"context"
|
2015-10-15 17:21:11 -04:00
|
|
|
"errors"
|
2016-02-10 18:23:41 -05:00
|
|
|
"io"
|
2019-04-09 14:26:27 -04:00
|
|
|
"log"
|
2015-10-15 17:21:11 -04:00
|
|
|
"net"
|
2019-04-09 14:26:27 -04:00
|
|
|
"strconv"
|
2015-10-15 17:21:11 -04:00
|
|
|
"strings"
|
2018-12-18 14:38:53 -05:00
|
|
|
"time"
|
2015-10-15 17:21:11 -04:00
|
|
|
|
2019-05-25 14:36:16 -04:00
|
|
|
"github.com/eyedeekay/sam3/i2pkeys"
|
2019-04-09 14:26:27 -04:00
|
|
|
)
|
|
|
|
|
2015-10-15 17:21:11 -04:00
|
|
|
// Represents a streaming session.
|
|
|
|
type StreamSession struct {
|
2019-05-25 14:36:16 -04:00
|
|
|
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
|
2018-12-18 14:38:53 -05:00
|
|
|
Timeout time.Duration
|
|
|
|
Deadline time.Time
|
2019-03-26 22:22:00 -04:00
|
|
|
sigType string
|
2019-03-26 23:04:25 -04:00
|
|
|
from string
|
|
|
|
to string
|
2019-02-13 23:58:24 -05:00
|
|
|
}
|
|
|
|
|
2019-03-26 23:09:53 -04:00
|
|
|
func (ss *StreamSession) From() string {
|
|
|
|
return ss.from
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ss *StreamSession) To() string {
|
|
|
|
return ss.to
|
|
|
|
}
|
|
|
|
|
2019-02-13 23:58:24 -05:00
|
|
|
func (ss *StreamSession) SignatureType() string {
|
2019-03-26 22:22:00 -04:00
|
|
|
return ss.sigType
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the local tunnel name of the I2P tunnel used for the stream session
|
2019-02-09 17:08:20 -05:00
|
|
|
func (ss *StreamSession) ID() string {
|
2015-10-15 17:21:11 -04:00
|
|
|
return ss.id
|
|
|
|
}
|
|
|
|
|
2019-02-09 17:08:20 -05:00
|
|
|
func (ss *StreamSession) Close() error {
|
2016-02-10 18:23:41 -05:00
|
|
|
return ss.conn.Close()
|
2015-12-14 17:13:45 -05:00
|
|
|
}
|
|
|
|
|
2015-10-15 17:21:11 -04:00
|
|
|
// Returns the I2P destination (the address) of the stream session
|
2019-05-25 14:36:16 -04:00
|
|
|
func (ss *StreamSession) Addr() i2pkeys.I2PAddr {
|
2015-10-15 17:21:11 -04:00
|
|
|
return ss.keys.Addr()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the keys associated with the stream session
|
2019-05-25 14:36:16 -04:00
|
|
|
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
|
2015-10-15 17:21:11 -04:00
|
|
|
return ss.keys
|
|
|
|
}
|
|
|
|
|
2016-02-10 18:23:41 -05:00
|
|
|
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
2015-10-15 17:21:11 -04:00
|
|
|
// specified. See the I2P documentation for a full list of options.
|
2019-05-25 14:36:16 -04:00
|
|
|
func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
|
2015-10-15 17:21:11 -04:00
|
|
|
conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-10 00:47:41 -04:00
|
|
|
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
|
2019-02-13 23:58:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
|
|
|
// specified. See the I2P documentation for a full list of options.
|
2019-05-25 14:36:16 -04:00
|
|
|
func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
|
2019-02-13 23:58:24 -05:00
|
|
|
conn, err := sam.newGenericSessionWithSignature("STREAM", id, keys, sigType, options, []string{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-10 00:47:41 -04:00
|
|
|
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, "0", "0"}, nil
|
2019-03-26 23:04:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
|
|
|
// specified. See the I2P documentation for a full list of options.
|
2019-05-25 14:36:16 -04:00
|
|
|
func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
|
2019-03-26 23:04:25 -04:00
|
|
|
conn, err := sam.newGenericSessionWithSignatureAndPorts("STREAM", id, from, to, keys, sigType, options, []string{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-04-10 00:47:41 -04:00
|
|
|
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, from, to}, nil
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|
|
|
|
|
2015-12-24 12:59:57 -05:00
|
|
|
// lookup name, convienence function
|
2019-05-25 14:36:16 -04:00
|
|
|
func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
|
2016-02-10 18:23:41 -05:00
|
|
|
sam, err := NewSAM(s.samAddr)
|
|
|
|
if err == nil {
|
|
|
|
addr, err := sam.Lookup(name)
|
|
|
|
sam.Close()
|
|
|
|
return addr, err
|
|
|
|
}
|
2019-05-25 14:36:16 -04:00
|
|
|
return i2pkeys.I2PAddr(""), err
|
2015-12-24 12:59:57 -05:00
|
|
|
}
|
|
|
|
|
2018-11-27 21:36:33 -05:00
|
|
|
// context-aware dialer, eventually...
|
2018-09-26 11:27:12 -04:00
|
|
|
func (s *StreamSession) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
|
2018-12-18 14:38:53 -05:00
|
|
|
return s.DialContextI2P(ctx, n, addr)
|
2018-09-26 11:27:12 -04:00
|
|
|
}
|
|
|
|
|
2018-11-27 21:46:02 -05:00
|
|
|
// context-aware dialer, eventually...
|
2018-11-27 21:56:34 -05:00
|
|
|
func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SAMConn, error) {
|
2018-12-18 14:38:53 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-25 14:36:16 -04:00
|
|
|
i2paddr, err := i2pkeys.NewI2PAddrFromString(addr)
|
2018-11-27 21:56:58 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-11-27 21:56:34 -05:00
|
|
|
return s.DialI2P(i2paddr)
|
2018-11-27 21:46:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-18 14:38:53 -05:00
|
|
|
/*
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-12-14 13:58:45 -05:00
|
|
|
// implement net.Dialer
|
|
|
|
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
|
|
|
|
|
2019-05-25 14:36:16 -04:00
|
|
|
var i2paddr i2pkeys.I2PAddr
|
2016-02-10 18:23:41 -05:00
|
|
|
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
|
2016-02-10 19:16:00 -05:00
|
|
|
i2paddr, err = s.Lookup(host)
|
2016-02-10 18:23:41 -05:00
|
|
|
} else {
|
|
|
|
// probably a destination
|
2019-05-25 14:36:16 -04:00
|
|
|
i2paddr = i2pkeys.I2PAddr(host)
|
2016-02-10 18:23:41 -05:00
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
return s.DialI2P(i2paddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
2015-12-14 13:58:45 -05:00
|
|
|
}
|
|
|
|
|
2015-10-15 17:21:11 -04:00
|
|
|
// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
|
2019-05-25 14:36:16 -04:00
|
|
|
func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
|
2015-10-15 17:21:11 -04:00
|
|
|
sam, err := NewSAM(s.samAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
conn := sam.conn
|
2019-10-22 02:05:58 -04:00
|
|
|
_, err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " FROM_PORT=" + s.from + " TO_PORT=" + s.to + " DESTINATION=" + addr.Base64() + " SILENT=false\r\n"))
|
2015-10-15 17:21:11 -04:00
|
|
|
if err != nil {
|
2016-02-05 16:34:03 -05:00
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buf := make([]byte, 4096)
|
|
|
|
n, err := conn.Read(buf)
|
2016-02-10 17:36:15 -05:00
|
|
|
if err != nil && err != io.EOF {
|
2016-02-05 16:34:03 -05:00
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
scanner := bufio.NewScanner(bytes.NewReader(buf[:n]))
|
|
|
|
scanner.Split(bufio.ScanWords)
|
|
|
|
for scanner.Scan() {
|
|
|
|
switch scanner.Text() {
|
2016-02-10 18:23:41 -05:00
|
|
|
case "STREAM":
|
2015-10-15 17:21:11 -04:00
|
|
|
continue
|
2016-02-10 18:23:41 -05:00
|
|
|
case "STATUS":
|
2015-10-15 17:21:11 -04:00
|
|
|
continue
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=OK":
|
2019-04-09 14:26:27 -04:00
|
|
|
return &SAMConn{s.keys.Addr(), addr, conn}, nil
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=CANT_REACH_PEER":
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("Can not reach peer")
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=I2P_ERROR":
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("I2P internal error")
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=INVALID_KEY":
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("Invalid key")
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=INVALID_ID":
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("Invalid tunnel ID")
|
2016-02-10 18:23:41 -05:00
|
|
|
case "RESULT=TIMEOUT":
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("Timeout")
|
2016-02-10 18:23:41 -05:00
|
|
|
default:
|
|
|
|
conn.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic("sam3 go library error in StreamSession.DialI2P()")
|
|
|
|
}
|
|
|
|
|
2015-12-13 11:47:25 -05:00
|
|
|
// create a new stream listener to accept inbound connections
|
2015-10-15 17:21:11 -04:00
|
|
|
func (s *StreamSession) Listen() (*StreamListener, error) {
|
2016-02-10 18:23:41 -05:00
|
|
|
return &StreamListener{
|
|
|
|
session: s,
|
|
|
|
id: s.id,
|
|
|
|
laddr: s.keys.Addr(),
|
|
|
|
}, nil
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|
|
|
|
|
2015-12-13 11:47:25 -05:00
|
|
|
type StreamListener struct {
|
2016-02-10 18:23:41 -05:00
|
|
|
// parent stream session
|
|
|
|
session *StreamSession
|
|
|
|
// our session id
|
|
|
|
id string
|
|
|
|
// our local address for this sam socket
|
2019-05-25 14:36:16 -04:00
|
|
|
laddr i2pkeys.I2PAddr
|
2015-11-30 12:38:18 -05:00
|
|
|
}
|
|
|
|
|
2019-03-26 23:23:37 -04:00
|
|
|
func (l *StreamListener) From() string {
|
|
|
|
return l.session.from
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *StreamListener) To() string {
|
|
|
|
return l.session.to
|
|
|
|
}
|
|
|
|
|
2015-12-13 11:47:25 -05:00
|
|
|
// get our address
|
|
|
|
// implements net.Listener
|
|
|
|
func (l *StreamListener) Addr() net.Addr {
|
2016-02-10 18:23:41 -05:00
|
|
|
return l.laddr
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|
|
|
|
|
2015-12-13 11:47:25 -05:00
|
|
|
// implements net.Listener
|
2015-10-15 17:21:11 -04:00
|
|
|
func (l *StreamListener) Close() error {
|
2016-02-10 18:23:41 -05:00
|
|
|
return l.session.Close()
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|
|
|
|
|
2015-12-13 11:47:25 -05:00
|
|
|
// implements net.Listener
|
|
|
|
func (l *StreamListener) Accept() (net.Conn, error) {
|
2016-02-10 18:23:41 -05:00
|
|
|
return l.AcceptI2P()
|
2015-12-22 12:10:29 -05:00
|
|
|
}
|
|
|
|
|
2019-04-09 14:26:27 -04:00
|
|
|
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]
|
|
|
|
}
|
|
|
|
|
2015-12-22 12:10:29 -05:00
|
|
|
// accept a new inbound connection
|
|
|
|
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
|
2016-02-10 18:23:41 -05:00
|
|
|
s, err := NewSAM(l.session.samAddr)
|
2016-05-09 06:52:54 -04:00
|
|
|
if err == nil {
|
2016-02-10 18:23:41 -05:00
|
|
|
// we connected to sam
|
|
|
|
// send accept() command
|
2019-10-22 02:05:58 -04:00
|
|
|
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\r\n")
|
2016-02-10 18:23:41 -05:00
|
|
|
// read reply
|
|
|
|
rd := bufio.NewReader(s.conn)
|
|
|
|
// read first line
|
|
|
|
line, err := rd.ReadString(10)
|
2019-04-09 14:26:27 -04:00
|
|
|
log.Println(line)
|
2016-02-10 18:23:41 -05:00
|
|
|
if err == nil {
|
|
|
|
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
|
|
|
|
// we gud read destination line
|
2019-04-09 14:26:27 -04:00
|
|
|
destline, err := rd.ReadString(10)
|
|
|
|
log.Println(destline)
|
2016-02-10 18:23:41 -05:00
|
|
|
if err == nil {
|
2019-04-09 14:26:27 -04:00
|
|
|
dest := ExtractDest(destline)
|
|
|
|
l.session.from = ExtractPairString(destline, "FROM_PORT")
|
|
|
|
l.session.to = ExtractPairString(destline, "TO_PORT")
|
2016-02-10 18:23:41 -05:00
|
|
|
// return wrapped connection
|
|
|
|
dest = strings.Trim(dest, "\n")
|
|
|
|
return &SAMConn{
|
|
|
|
laddr: l.laddr,
|
2019-05-25 14:36:16 -04:00
|
|
|
raddr: i2pkeys.I2PAddr(dest),
|
2016-02-10 18:23:41 -05:00
|
|
|
conn: s.conn,
|
|
|
|
}, nil
|
|
|
|
} else {
|
|
|
|
s.Close()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s.Close()
|
|
|
|
return nil, errors.New("invalid sam line: " + line)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s.Close()
|
2016-05-09 06:52:54 -04:00
|
|
|
return nil, err
|
2016-02-10 18:23:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
s.Close()
|
|
|
|
return nil, err
|
2015-10-15 17:21:11 -04:00
|
|
|
}
|