54 Commits

Author SHA1 Message Date
eyedeekay
7fc3116088 Bump version 2024-11-13 14:41:50 -05:00
eyedeekay
d3fb670d66 Fix go mods 2024-11-13 14:40:27 -05:00
eyedeekay
25751504b9 Fix up import paths 2024-11-09 11:54:54 -05:00
eyedeekay
a745742ee1 setup auto-assign workflow 2024-11-08 15:01:19 -05:00
eyedeekay
def28bbf7c Merge branch 'master' of github.com:eyedeekay/sam3 2024-11-08 12:55:50 -05:00
eyedeekay
e6c161ed99 change module path 2024-11-08 12:54:40 -05:00
idk
3ebfb85f8a Merge pull request #13 from hkh4n/refactor
Refactor
2024-11-03 21:42:55 +00:00
Haris Khan
14d0b22a28 add Read() and Write() methods 2024-11-02 23:26:10 -04:00
Haris Khan
67554060fb minor typo fix 2024-11-02 22:20:52 -04:00
Haris Khan
8b09ca7502 more uniform naming 2024-11-02 21:57:11 -04:00
idk
97d1c812d3 Merge pull request #12 from hkh4n/logging
logging naming convention hotfix
2024-10-23 17:34:38 +00:00
Haris Khan
5be3e27599 Attempt to fix naming collision with other libs 2024-10-23 00:03:16 -04:00
Haris Khan
ecba767d91 grammar 2024-10-17 14:51:19 -04:00
idk
5149b7e504 Merge pull request #11 from hkh4n/logging
Added logging
2024-10-17 18:51:15 +00:00
Haris Khan
67c0c9288a Updated README.md to reflect logging 2024-10-17 14:42:19 -04:00
Haris Khan
10f42af061 .Info -> .Debug 2024-10-16 17:21:44 -04:00
Haris Khan
aa63210c3c WORKING FIX 2024-10-16 11:23:49 -04:00
Haris Khan
4e1b426230 name collision 2024-10-16 10:51:21 -04:00
Haris Khan
a372049be9 added logging to suggestedOptions.go
-fixed name collision in stream_test.go
2024-10-16 10:35:43 -04:00
Haris Khan
6e2cc71a92 added logging to streamListener.go
-added dest in ExtractDest()
2024-10-16 10:24:30 -04:00
Haris Khan
988769ed5a fixed name collision in primary_stream_test.go 2024-10-16 10:18:06 -04:00
Haris Khan
504b7fb48b added logging to stream.go
-added better error handling to Lookup()
2024-10-16 10:14:57 -04:00
Haris Khan
c10b6b284c finished up sam3.go 2024-10-16 10:02:47 -04:00
Haris Khan
c2ad35a952 added logging to sam3.go & bumped i2pkeys v0.33.7 -> v0.33.8
-removed name collisions with "log"
2024-10-15 23:01:00 -04:00
Haris Khan
9e6d0489cf added logging to resolver.go 2024-10-15 22:26:33 -04:00
Haris Khan
d05428754b added logging to raw.go 2024-10-15 22:20:33 -04:00
Haris Khan
41d9bd0150 Merge branch 'master' into logging
# Conflicts:
#	go.mod
#	go.sum
2024-10-15 22:10:49 -04:00
Haris Khan
fd22f227b1 finished up primary.go 2024-10-15 22:09:18 -04:00
Haris Khan
0415adf35d added logging to primary.go
-added fromPort, toPort in NewUniqueStreamSubsession
2024-10-15 21:01:36 -04:00
Haris Khan
3655462ca4 added logging to emit-options.go 2024-10-15 17:01:43 -04:00
Haris Khan
4bb6f81c40 added logging to emit.go 2024-10-15 12:54:17 -04:00
Haris Khan
21e7b5a177 added logging to datagram.go 2024-10-15 12:39:20 -04:00
Haris Khan
4166a2c827 added log.go & logging for config.go 2024-10-15 12:23:24 -04:00
eyedeekay
cd2a4f072e update i2pkeys library to new version 2024-09-17 19:40:10 -04:00
eyedeekay
818b5249bc Add credit for contribution to release info 2024-09-17 19:35:15 -04:00
idk
83f9866de4 Merge pull request #10 from hkh4n/refactor
Refactored error handling in sam3.go
2024-09-12 12:26:56 -04:00
Haris Khan
b4293f755e refactored error handling in NewKeys() and newGenericSessionWithSignatureAndPorts() 2024-09-10 19:55:28 -04:00
Haris Khan
2bde2687b3 refactored error handling in NewSAM(), included "fmt" 2024-09-10 19:21:06 -04:00
eyedeekay
19d8d8e4a8 Update module to i2pkeys@v0.33.7 2024-01-09 14:45:40 -05:00
eyedeekay
1cec982a61 bump version 0.33.7 2024-01-09 14:22:29 -05:00
eyedeekay
b2df466212 Don't leave errors unchecked. gofmt again. 2024-01-09 14:20:35 -05:00
eyedeekay
bf2a12d78d fix misspellings 2024-01-09 13:40:42 -05:00
eyedeekay
760e0b44b2 gofmt -s 2024-01-09 13:37:49 -05:00
eyedeekay
9b7a798782 Add a space when specifying the port to a datagram session 2024-01-09 13:34:49 -05:00
eyedeekay
3dc49e391d force an enctype if one is not present 2024-01-07 12:09:13 -05:00
eyedeekay
b08d519a17 bump version 2023-07-21 15:10:09 -04:00
idk
256aaa7430 update go modules 2023-03-07 02:19:24 +00:00
idk
197aca0ece Never pass Sig_NONE again, if sig is unset, use the correct default sig 2023-01-16 04:18:11 +00:00
idk
fff37dbffa Never pass Sig_NONE again, if sig is unset, use the correct default sig 2023-01-16 04:17:51 +00:00
idk
dfd7cd886f update index.html 2022-08-28 13:40:47 -04:00
idk
c998e57a89 update index.html 2022-08-28 13:38:27 -04:00
idk
41317685c5 update index.html 2022-08-28 13:36:46 -04:00
idk
56cca2e537 bump version 2022-08-08 18:09:36 -04:00
idk
9ca67baa32 don't let the primary session thing be a guess, detect first, then attempt one and cache the result 2022-08-08 17:43:43 -04:00
25 changed files with 847 additions and 247 deletions

20
.github/workflows/auto-assign.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Auto Assign
on:
issues:
types: [opened]
pull_request:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: 'Auto-assign issue'
uses: pozil/auto-assign-issue@v1
with:
repo-token:${{ secrets.GITHUB_TOKEN }}
assignees: eyedeekay
numOfAssignee: 1

View File

@@ -1,13 +1,14 @@
USER_GH=eyedeekay
VERSION=0.33.3
VERSION=0.33.9
CREDIT='contributors to this release: @hkh4n, @eyedeekay'
packagename=sam3
echo:
@echo "type make version to do release $(VERSION)"
version:
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION) $(CREDIT)"
del:
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)

View File

@@ -10,6 +10,8 @@ before upgrading your sam3 dependencies. You can probably do this by running:
find . -name '*.go' -exec sed -i 's|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g' {} \;
```
STATUS: This project is maintained. I will respond to issues, pull requests, and feature requests within a few days.
# README #
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/samv3) bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
@@ -94,6 +96,26 @@ Error handling was omitted in the above code for readability.
* `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
## Verbosity ##
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
There are three available log levels:
- Debug
```shell
export DEBUG_I2P=debug
```
- Warn
```shell
export DEBUG_I2P=warn
```
- Error
```shell
export DEBUG_I2P=error
```
If DEBUG_I2P is set to an unrecognized variable, it will fall back to "debug".
## License ##
Public domain.

View File

@@ -4,12 +4,12 @@ import (
"net"
"time"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
/*
import (
. "github.com/eyedeekay/i2pkeys"
. "github.com/go-i2p/i2pkeys"
)
*/
// Implements net.Conn

View File

@@ -2,12 +2,13 @@ package sam3
import (
"fmt"
"github.com/sirupsen/logrus"
"math/rand"
"net"
"strconv"
"strings"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
// I2PConfig is a struct which manages I2P configuration options
@@ -51,6 +52,8 @@ type I2PConfig struct {
ReduceIdle string
ReduceIdleTime string
ReduceIdleQuantity string
LeaseSetEncryption string
//Streaming Library options
AccessListType string
AccessList []string
@@ -65,6 +68,10 @@ func (f *I2PConfig) Sam() string {
if f.SamPort != "" {
port = f.SamPort
}
log.WithFields(logrus.Fields{
"host": host,
"port": port,
}).Debug("SAM address constructed")
return host + ":" + port
}
@@ -78,6 +85,10 @@ func (f *I2PConfig) SetSAMAddress(addr string) {
}
f.SamPort = "7656"
f.SamHost = "127.0.0.1"
log.WithFields(logrus.Fields{
"host": f.SamHost,
"port": f.SamPort,
}).Debug("SAM address set")
}
func (f *I2PConfig) ID() string {
@@ -87,6 +98,7 @@ func (f *I2PConfig) ID() string {
b[i] = "abcdefghijklmnopqrstuvwxyz"[rand.Intn(len("abcdefghijklmnopqrstuvwxyz"))]
}
f.TunName = string(b)
log.WithField("TunName", f.TunName).Debug("Generated random tunnel name")
}
return " ID=" + f.TunName + " "
}
@@ -102,100 +114,139 @@ func (f *I2PConfig) Leasesetsettings() (string, string, string) {
if f.LeaseSetPrivateSigningKey != "" {
t = " i2cp.leaseSetPrivateSigningKey=" + f.LeaseSetPrivateSigningKey + " "
}
log.WithFields(logrus.Fields{
"leaseSetKey": r,
"leaseSetPrivateKey": s,
"leaseSetPrivateSigningKey": t,
}).Debug("Lease set settings constructed")
return r, s, t
}
func (f *I2PConfig) FromPort() string {
if f.samMax() < 3.1 {
log.Debug("SAM version < 3.1, FromPort not applicable")
return ""
}
if f.Fromport != "0" {
log.WithField("fromPort", f.Fromport).Debug("FromPort set")
return " FROM_PORT=" + f.Fromport + " "
}
log.Debug("FromPort not set")
return ""
}
func (f *I2PConfig) ToPort() string {
if f.samMax() < 3.1 {
log.Debug("SAM version < 3.1, ToPort not applicable")
return ""
}
if f.Toport != "0" {
log.WithField("toPort", f.Toport).Debug("ToPort set")
return " TO_PORT=" + f.Toport + " "
}
log.Debug("ToPort not set")
return ""
}
func (f *I2PConfig) SessionStyle() string {
if f.Style != "" {
log.WithField("style", f.Style).Debug("Session style set")
return " STYLE=" + f.Style + " "
}
log.Debug("Using default STREAM style")
return " STYLE=STREAM "
}
func (f *I2PConfig) samMax() float64 {
i, err := strconv.Atoi(f.SamMax)
if err != nil {
log.WithError(err).Warn("Failed to parse SamMax, using default 3.1")
return 3.1
}
log.WithField("samMax", float64(i)).Debug("SAM max version parsed")
return float64(i)
}
func (f *I2PConfig) MinSAM() string {
if f.SamMin == "" {
log.Debug("Using default MinSAM: 3.0")
return "3.0"
}
log.WithField("minSAM", f.SamMin).Debug("MinSAM set")
return f.SamMin
}
func (f *I2PConfig) MaxSAM() string {
if f.SamMax == "" {
log.Debug("Using default MaxSAM: 3.1")
return "3.1"
}
log.WithField("maxSAM", f.SamMax).Debug("MaxSAM set")
return f.SamMax
}
func (f *I2PConfig) DestinationKey() string {
if &f.DestinationKeys != nil {
log.WithField("destinationKey", f.DestinationKeys.String()).Debug("Destination key set")
return " DESTINATION=" + f.DestinationKeys.String() + " "
}
log.Debug("Using TRANSIENT destination")
return " DESTINATION=TRANSIENT "
}
func (f *I2PConfig) SignatureType() string {
if f.samMax() < 3.1 {
log.Debug("SAM version < 3.1, SignatureType not applicable")
return ""
}
if f.SigType != "" {
log.WithField("sigType", f.SigType).Debug("Signature type set")
return " SIGNATURE_TYPE=" + f.SigType + " "
}
log.Debug("Signature type not set")
return ""
}
func (f *I2PConfig) EncryptLease() string {
if f.EncryptLeaseSet == "true" {
log.Debug("Lease set encryption enabled")
return " i2cp.encryptLeaseSet=true "
}
log.Debug("Lease set encryption not enabled")
return ""
}
func (f *I2PConfig) Reliability() string {
if f.MessageReliability != "" {
log.WithField("reliability", f.MessageReliability).Debug("Message reliability set")
return " i2cp.messageReliability=" + f.MessageReliability + " "
}
log.Debug("Message reliability not set")
return ""
}
func (f *I2PConfig) Reduce() string {
if f.ReduceIdle == "true" {
log.WithFields(logrus.Fields{
"reduceIdle": f.ReduceIdle,
"reduceIdleTime": f.ReduceIdleTime,
"reduceIdleQuantity": f.ReduceIdleQuantity,
}).Debug("Reduce idle settings applied")
return "i2cp.reduceOnIdle=" + f.ReduceIdle + "i2cp.reduceIdleTime=" + f.ReduceIdleTime + "i2cp.reduceQuantity=" + f.ReduceIdleQuantity
}
log.Debug("Reduce idle settings not applied")
return ""
}
func (f *I2PConfig) Close() string {
if f.CloseIdle == "true" {
log.WithFields(logrus.Fields{
"closeIdle": f.CloseIdle,
"closeIdleTime": f.CloseIdleTime,
}).Debug("Close idle settings applied")
return "i2cp.closeOnIdle=" + f.CloseIdle + "i2cp.closeIdleTime=" + f.CloseIdleTime
}
log.Debug("Close idle settings not applied")
return ""
}
@@ -210,6 +261,7 @@ func (f *I2PConfig) DoZero() string {
if f.FastRecieve == "true" {
r += " " + f.FastRecieve + " "
}
log.WithField("zeroHopSettings", r).Debug("Zero hop settings applied")
return r
}
func (f *I2PConfig) Print() []string {
@@ -234,17 +286,22 @@ func (f *I2PConfig) Print() []string {
lsk, lspk, lspsk,
f.Accesslisttype(),
f.Accesslist(),
f.LeaseSetEncryptionType(),
}
}
func (f *I2PConfig) Accesslisttype() string {
if f.AccessListType == "whitelist" {
log.Debug("Access list type set to whitelist")
return "i2cp.enableAccessList=true"
} else if f.AccessListType == "blacklist" {
log.Debug("Access list type set to blacklist")
return "i2cp.enableBlackList=true"
} else if f.AccessListType == "none" {
log.Debug("Access list type set to none")
return ""
}
log.Debug("Access list type not set")
return ""
}
@@ -254,11 +311,28 @@ func (f *I2PConfig) Accesslist() string {
for _, s := range f.AccessList {
r += s + ","
}
log.WithField("accessList", r).Debug("Access list generated")
return "i2cp.accessList=" + strings.TrimSuffix(r, ",")
}
log.Debug("Access list not set")
return ""
}
func (f *I2PConfig) LeaseSetEncryptionType() string {
if f.LeaseSetEncryption == "" {
log.Debug("Using default lease set encryption type: 4,0")
return "i2cp.leaseSetEncType=4,0"
}
for _, s := range strings.Split(f.LeaseSetEncryption, ",") {
if _, err := strconv.Atoi(s); err != nil {
log.WithField("invalidType", s).Panic("Invalid encrypted leaseSet type")
//panic("Invalid encrypted leaseSet type: " + s)
}
}
log.WithField("leaseSetEncType", f.LeaseSetEncryption).Debug("Lease set encryption type set")
return "i2cp.leaseSetEncType=" + f.LeaseSetEncryption
}
func NewConfig(opts ...func(*I2PConfig) error) (*I2PConfig, error) {
var config I2PConfig
config.SamHost = "127.0.0.1"

View File

@@ -3,11 +3,12 @@ package sam3
import (
"bytes"
"errors"
"github.com/sirupsen/logrus"
"net"
"strconv"
"time"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
@@ -27,67 +28,99 @@ type DatagramSession struct {
// 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.I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"id": id,
"udpPort": udpPort,
}).Debug("Creating new DatagramSession")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{"PORT=" + lport})
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{" PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create generic session")
return nil, err
}
log.WithField("id", id).Info("DatagramSession created successfully")
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil}, nil
}
func (s *DatagramSession) B32() string {
return s.keys.Addr().Base32()
b32 := s.keys.Addr().Base32()
log.WithField("b32", b32).Debug("Generated B32 address")
return b32
}
func (s *DatagramSession) Dial(net string, addr string) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing address")
netaddr, err := s.Lookup(addr)
if err != nil {
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialRemote(net, addr string) (net.PacketConn, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing remote address")
netaddr, err := s.Lookup(addr)
if err != nil {
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing I2P remote address")
switch addr.(type) {
case *i2pkeys.I2PAddr:
s.remoteAddr = addr.(*i2pkeys.I2PAddr)
@@ -99,6 +132,7 @@ func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSes
}
func (s *DatagramSession) RemoteAddr() net.Addr {
log.WithField("remoteAddr", s.remoteAddr).Debug("Getting remote address")
return s.remoteAddr
}
@@ -106,6 +140,7 @@ func (s *DatagramSession) RemoteAddr() net.Addr {
// the number of bytes read, from what address it was sent, or an error.
// implements net.PacketConn
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
log.Debug("Reading datagram")
// extra bytes to read the remote address of incomming datagram
buf := make([]byte, len(b)+4096)
@@ -114,6 +149,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 {
log.WithError(err).Error("Failed to read from UDP")
return 0, i2pkeys.I2PAddr(""), err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
@@ -123,10 +159,12 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
}
i := bytes.IndexByte(buf, byte('\n'))
if i > 4096 || i > n {
log.Error("Could not parse incoming message remote address")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address.")
}
raddr, err := i2pkeys.NewI2PAddrFromString(string(buf[:i]))
if err != nil {
log.WithError(err).Error("Could not parse incoming message remote address")
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
@@ -135,15 +173,18 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
return n - (i + 1), raddr, errors.New("Datagram did not fit into your buffer.")
} else {
copy(b, buf[i+1:n])
log.WithField("bytesRead", n-(i+1)).Debug("Datagram read successfully")
return n - (i + 1), raddr, nil
}
}
func (s *DatagramSession) Accept() (net.Conn, error) {
log.Debug("Accept called on DatagramSession")
return s, nil
}
func (s *DatagramSession) Read(b []byte) (n int, err error) {
log.Debug("Reading from DatagramSession")
rint, _, rerr := s.ReadFrom(b)
return rint, rerr
}
@@ -152,29 +193,46 @@ func (s *DatagramSession) Read(b []byte) (n int, err error) {
// 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) {
log.WithFields(logrus.Fields{
"addr": addr,
"datagramLen": len(b),
}).Debug("Writing datagram")
header := []byte("3.1 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to write to UDP")
} else {
log.WithField("bytesWritten", n).Debug("Datagram written successfully")
}
return n, err
}
func (s *DatagramSession) Write(b []byte) (int, error) {
log.WithField("dataLen", len(b)).Debug("Writing to DatagramSession")
return s.WriteTo(b, s.remoteAddr)
}
// Closes the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) Close() error {
log.Debug("Closing DatagramSession")
err := s.conn.Close()
err2 := s.udpconn.Close()
if err != nil {
log.WithError(err).Error("Failed to close connection")
return err
}
if err2 != nil {
log.WithError(err2).Error("Failed to close UDP connection")
}
return err2
}
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
addr := s.keys.Addr()
log.WithField("localI2PAddr", addr).Debug("Getting local I2P address")
return addr
}
// Implements net.PacketConn
@@ -187,12 +245,14 @@ func (s *DatagramSession) Addr() net.Addr {
}
func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
log.WithField("name", name).Debug("Looking up address")
var sam *SAM
sam, err = NewSAM(s.samAddr)
if err == nil {
defer sam.Close()
a, err = sam.Lookup(name)
}
log.WithField("address", a).Debug("Lookup successful")
return
}
@@ -200,19 +260,23 @@ func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
// net.PacketConn and does the same thing. Setting write deadlines for datagrams
// is seldom done.
func (s *DatagramSession) SetDeadline(t time.Time) error {
log.WithField("deadline", t).Debug("Setting deadline")
return s.udpconn.SetDeadline(t)
}
// Sets read deadline for the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) SetReadDeadline(t time.Time) error {
log.WithField("readDeadline", t).Debug("Setting read deadline")
return s.udpconn.SetReadDeadline(t)
}
// Sets the write deadline for the DatagramSession. Implements net.Packetconn.
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
log.WithField("writeDeadline", t).Debug("Setting write deadline")
return s.udpconn.SetWriteDeadline(t)
}
func (s *DatagramSession) SetWriteBuffer(bytes int) error {
log.WithField("bytes", bytes).Debug("Setting write buffer")
return s.udpconn.SetWriteBuffer(bytes)
}

View File

@@ -2,7 +2,6 @@ package sam3
import (
"fmt"
"log"
"testing"
"time"
)
@@ -123,7 +122,7 @@ func ExampleDatagramSession() {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return

View File

@@ -2,26 +2,31 @@ package sam3
import (
"fmt"
"github.com/sirupsen/logrus"
"strconv"
"strings"
)
//Option is a SAMEmit Option
// Option is a SAMEmit Option
type Option func(*SAMEmit) error
//SetType sets the type of the forwarder server
// SetType sets the type of the forwarder server
func SetType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "STREAM" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
} else if s == "DATAGRAM" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
} else if s == "RAW" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
}
log.WithField("style", s).Error("Invalid session style")
return fmt.Errorf("Invalid session STYLE=%s, must be STREAM, DATAGRAM, or RAW", s)
}
}
@@ -31,136 +36,162 @@ func SetSAMAddress(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
sp := strings.Split(s, ":")
if len(sp) > 2 {
log.WithField("address", s).Error("Invalid SAM address")
return fmt.Errorf("Invalid address string: %s", sp)
}
if len(sp) == 2 {
c.I2PConfig.SamPort = sp[1]
}
c.I2PConfig.SamHost = sp[0]
log.WithFields(logrus.Fields{
"host": c.I2PConfig.SamHost,
"port": c.I2PConfig.SamPort,
}).Debug("Set SAM address")
return nil
}
}
//SetSAMHost sets the host of the SAMEmit's SAM bridge
// SetSAMHost sets the host of the SAMEmit's SAM bridge
func SetSAMHost(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.SamHost = s
log.WithField("host", s).Debug("Set SAM host")
return nil
}
}
//SetSAMPort sets the port of the SAMEmit's SAM bridge using a string
// SetSAMPort sets the port of the SAMEmit's SAM bridge using a string
func SetSAMPort(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
port, err := strconv.Atoi(s)
if err != nil {
log.WithField("port", s).Error("Invalid SAM port: non-number")
return fmt.Errorf("Invalid SAM Port %s; non-number", s)
}
if port < 65536 && port > -1 {
c.I2PConfig.SamPort = s
log.WithField("port", s).Debug("Set SAM port")
return nil
}
log.WithField("port", port).Error("Invalid SAM port")
return fmt.Errorf("Invalid port")
}
}
//SetName sets the host of the SAMEmit's SAM bridge
// SetName sets the host of the SAMEmit's SAM bridge
func SetName(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.TunName = s
log.WithField("name", s).Debug("Set tunnel name")
return nil
}
}
//SetInLength sets the number of hops inbound
// SetInLength sets the number of hops inbound
func SetInLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.InLength = strconv.Itoa(u)
log.WithField("inLength", u).Debug("Set inbound tunnel length")
return nil
}
log.WithField("inLength", u).Error("Invalid inbound tunnel length")
return fmt.Errorf("Invalid inbound tunnel length")
}
}
//SetOutLength sets the number of hops outbound
// SetOutLength sets the number of hops outbound
func SetOutLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.OutLength = strconv.Itoa(u)
log.WithField("outLength", u).Debug("Set outbound tunnel length")
return nil
}
log.WithField("outLength", u).Error("Invalid outbound tunnel length")
return fmt.Errorf("Invalid outbound tunnel length")
}
}
//SetInVariance sets the variance of a number of hops inbound
// SetInVariance sets the variance of a number of hops inbound
func SetInVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.InVariance = strconv.Itoa(i)
log.WithField("inVariance", i).Debug("Set inbound tunnel variance")
return nil
}
log.WithField("inVariance", i).Error("Invalid inbound tunnel variance")
return fmt.Errorf("Invalid inbound tunnel length")
}
}
//SetOutVariance sets the variance of a number of hops outbound
// SetOutVariance sets the variance of a number of hops outbound
func SetOutVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.OutVariance = strconv.Itoa(i)
log.WithField("outVariance", i).Debug("Set outbound tunnel variance")
return nil
}
log.WithField("outVariance", i).Error("Invalid outbound tunnel variance")
return fmt.Errorf("Invalid outbound tunnel variance")
}
}
//SetInQuantity sets the inbound tunnel quantity
// SetInQuantity sets the inbound tunnel quantity
func SetInQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.InQuantity = strconv.Itoa(u)
log.WithField("inQuantity", u).Debug("Set inbound tunnel quantity")
return nil
}
log.WithField("inQuantity", u).Error("Invalid inbound tunnel quantity")
return fmt.Errorf("Invalid inbound tunnel quantity")
}
}
//SetOutQuantity sets the outbound tunnel quantity
// SetOutQuantity sets the outbound tunnel quantity
func SetOutQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.OutQuantity = strconv.Itoa(u)
log.WithField("outQuantity", u).Debug("Set outbound tunnel quantity")
return nil
}
log.WithField("outQuantity", u).Error("Invalid outbound tunnel quantity")
return fmt.Errorf("Invalid outbound tunnel quantity")
}
}
//SetInBackups sets the inbound tunnel backups
// SetInBackups sets the inbound tunnel backups
func SetInBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.InBackupQuantity = strconv.Itoa(u)
log.WithField("inBackups", u).Debug("Set inbound tunnel backups")
return nil
}
log.WithField("inBackups", u).Error("Invalid inbound tunnel backup quantity")
return fmt.Errorf("Invalid inbound tunnel backup quantity")
}
}
//SetOutBackups sets the inbound tunnel backups
// SetOutBackups sets the inbound tunnel backups
func SetOutBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.OutBackupQuantity = strconv.Itoa(u)
log.WithField("outBackups", u).Debug("Set outbound tunnel backups")
return nil
}
log.WithField("outBackups", u).Error("Invalid outbound tunnel backup quantity")
return fmt.Errorf("Invalid outbound tunnel backup quantity")
}
}
//SetEncrypt tells the router to use an encrypted leaseset
// SetEncrypt tells the router to use an encrypted leaseset
func SetEncrypt(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -168,43 +199,48 @@ func SetEncrypt(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.EncryptLeaseSet = "false"
log.WithField("encrypt", b).Debug("Set lease set encryption")
return nil
}
}
//SetLeaseSetKey sets the host of the SAMEmit's SAM bridge
// SetLeaseSetKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetKey = s
log.WithField("leaseSetKey", s).Debug("Set lease set key")
return nil
}
}
//SetLeaseSetPrivateKey sets the host of the SAMEmit's SAM bridge
// SetLeaseSetPrivateKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateKey = s
log.WithField("leaseSetPrivateKey", s).Debug("Set lease set private key")
return nil
}
}
//SetLeaseSetPrivateSigningKey sets the host of the SAMEmit's SAM bridge
// SetLeaseSetPrivateSigningKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateSigningKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateSigningKey = s
log.WithField("leaseSetPrivateSigningKey", s).Debug("Set lease set private signing key")
return nil
}
}
//SetMessageReliability sets the host of the SAMEmit's SAM bridge
// SetMessageReliability sets the host of the SAMEmit's SAM bridge
func SetMessageReliability(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.MessageReliability = s
log.WithField("messageReliability", s).Debug("Set message reliability")
return nil
}
}
//SetAllowZeroIn tells the tunnel to accept zero-hop peers
// SetAllowZeroIn tells the tunnel to accept zero-hop peers
func SetAllowZeroIn(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -212,11 +248,12 @@ func SetAllowZeroIn(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.InAllowZeroHop = "false"
log.WithField("allowZeroIn", b).Debug("Set allow zero-hop inbound")
return nil
}
}
//SetAllowZeroOut tells the tunnel to accept zero-hop peers
// SetAllowZeroOut tells the tunnel to accept zero-hop peers
func SetAllowZeroOut(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -224,11 +261,12 @@ func SetAllowZeroOut(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.OutAllowZeroHop = "false"
log.WithField("allowZeroOut", b).Debug("Set allow zero-hop outbound")
return nil
}
}
//SetCompress tells clients to use compression
// SetCompress tells clients to use compression
func SetCompress(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -236,11 +274,12 @@ func SetCompress(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.UseCompression = "false"
log.WithField("compress", b).Debug("Set compression")
return nil
}
}
//SetFastRecieve tells clients to use compression
// SetFastRecieve tells clients to use compression
func SetFastRecieve(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -248,11 +287,12 @@ func SetFastRecieve(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.FastRecieve = "false"
log.WithField("fastReceive", b).Debug("Set fast receive")
return nil
}
}
//SetReduceIdle tells the connection to reduce it's tunnels during extended idle time.
// SetReduceIdle tells the connection to reduce it's tunnels during extended idle time.
func SetReduceIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -260,46 +300,54 @@ func SetReduceIdle(b bool) func(*SAMEmit) error {
return nil
}
c.I2PConfig.ReduceIdle = "false"
log.WithField("reduceIdle", b).Debug("Set reduce idle")
return nil
}
}
//SetReduceIdleTime sets the time to wait before reducing tunnels to idle levels
// SetReduceIdleTime sets the time to wait before reducing tunnels to idle levels
func SetReduceIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = "300000"
if u >= 6 {
c.I2PConfig.ReduceIdleTime = strconv.Itoa((u * 60) * 1000)
idleTime := strconv.Itoa((u * 60) * 1000)
c.I2PConfig.ReduceIdleTime = idleTime
log.WithField("reduceIdleTime", idleTime).Debug("Set reduce idle time")
return nil
}
log.WithField("minutes", u).Error("Invalid reduce idle timeout")
return fmt.Errorf("Invalid reduce idle timeout(Measured in minutes) %v", u)
}
}
//SetReduceIdleTimeMs sets the time to wait before reducing tunnels to idle levels in milliseconds
// SetReduceIdleTimeMs sets the time to wait before reducing tunnels to idle levels in milliseconds
func SetReduceIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = "300000"
if u >= 300000 {
c.I2PConfig.ReduceIdleTime = strconv.Itoa(u)
log.WithField("reduceIdleTimeMs", u).Debug("Set reduce idle time in milliseconds")
return nil
}
log.WithField("milliseconds", u).Error("Invalid reduce idle timeout")
return fmt.Errorf("Invalid reduce idle timeout(Measured in milliseconds) %v", u)
}
}
//SetReduceIdleQuantity sets minimum number of tunnels to reduce to during idle time
// SetReduceIdleQuantity sets minimum number of tunnels to reduce to during idle time
func SetReduceIdleQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 5 {
c.I2PConfig.ReduceIdleQuantity = strconv.Itoa(u)
log.WithField("reduceIdleQuantity", u).Debug("Set reduce idle quantity")
return nil
}
log.WithField("quantity", u).Error("Invalid reduce tunnel quantity")
return fmt.Errorf("Invalid reduce tunnel quantity")
}
}
//SetCloseIdle tells the connection to close it's tunnels during extended idle time.
// SetCloseIdle tells the connection to close it's tunnels during extended idle time.
func SetCloseIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
@@ -311,59 +359,72 @@ func SetCloseIdle(b bool) func(*SAMEmit) error {
}
}
//SetCloseIdleTime sets the time to wait before closing tunnels to idle levels
// SetCloseIdleTime sets the time to wait before closing tunnels to idle levels
func SetCloseIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = "300000"
if u >= 6 {
c.I2PConfig.CloseIdleTime = strconv.Itoa((u * 60) * 1000)
idleTime := strconv.Itoa((u * 60) * 1000)
c.I2PConfig.CloseIdleTime = idleTime
log.WithFields(logrus.Fields{
"minutes": u,
"milliseconds": idleTime,
}).Debug("Set close idle time")
return nil
}
log.WithField("minutes", u).Error("Invalid close idle timeout")
return fmt.Errorf("Invalid close idle timeout(Measured in minutes) %v", u)
}
}
//SetCloseIdleTimeMs sets the time to wait before closing tunnels to idle levels in milliseconds
// SetCloseIdleTimeMs sets the time to wait before closing tunnels to idle levels in milliseconds
func SetCloseIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = "300000"
if u >= 300000 {
c.I2PConfig.CloseIdleTime = strconv.Itoa(u)
log.WithField("closeIdleTimeMs", u).Debug("Set close idle time in milliseconds")
return nil
}
return fmt.Errorf("Invalid close idle timeout(Measured in milliseconds) %v", u)
}
}
//SetAccessListType tells the system to treat the AccessList as a whitelist
// SetAccessListType tells the system to treat the AccessList as a whitelist
func SetAccessListType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "whitelist" {
c.I2PConfig.AccessListType = "whitelist"
log.Debug("Set access list type to whitelist")
return nil
} else if s == "blacklist" {
c.I2PConfig.AccessListType = "blacklist"
log.Debug("Set access list type to blacklist")
return nil
} else if s == "none" {
c.I2PConfig.AccessListType = ""
log.Debug("Set access list type to none")
return nil
} else if s == "" {
c.I2PConfig.AccessListType = ""
log.Debug("Set access list type to none")
return nil
}
return fmt.Errorf("Invalid Access list type(whitelist, blacklist, none)")
}
}
//SetAccessList tells the system to treat the AccessList as a whitelist
// SetAccessList tells the system to treat the AccessList as a whitelist
func SetAccessList(s []string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if len(s) > 0 {
for _, a := range s {
c.I2PConfig.AccessList = append(c.I2PConfig.AccessList, a)
}
log.WithField("accessList", s).Debug("Set access list")
return nil
}
log.Debug("No access list set (empty list provided)")
return nil
}
}

49
emit.go
View File

@@ -2,7 +2,7 @@ package sam3
import (
"fmt"
"log"
"github.com/sirupsen/logrus"
"net"
"strings"
)
@@ -12,15 +12,15 @@ type SAMEmit struct {
}
func (e *SAMEmit) OptStr() string {
optStr := ""
for _, opt := range e.I2PConfig.Print() {
optStr += opt + " "
}
optStr := strings.Join(e.I2PConfig.Print(), " ")
log.WithField("optStr", optStr).Debug("Generated option string")
return optStr
}
func (e *SAMEmit) Hello() string {
return fmt.Sprintf("HELLO VERSION MIN=%s MAX=%s \n", e.I2PConfig.MinSAM(), e.I2PConfig.MaxSAM())
hello := fmt.Sprintf("HELLO VERSION MIN=%s MAX=%s \n", e.I2PConfig.MinSAM(), e.I2PConfig.MaxSAM())
log.WithField("hello", hello).Debug("Generated HELLO command")
return hello
}
func (e *SAMEmit) HelloBytes() []byte {
@@ -28,7 +28,9 @@ func (e *SAMEmit) HelloBytes() []byte {
}
func (e *SAMEmit) GenerateDestination() string {
return fmt.Sprintf("DEST GENERATE %s \n", e.I2PConfig.SignatureType())
dest := fmt.Sprintf("DEST GENERATE %s \n", e.I2PConfig.SignatureType())
log.WithField("destination", dest).Debug("Generated DEST GENERATE command")
return dest
}
func (e *SAMEmit) GenerateDestinationBytes() []byte {
@@ -36,7 +38,9 @@ func (e *SAMEmit) GenerateDestinationBytes() []byte {
}
func (e *SAMEmit) Lookup(name string) string {
return fmt.Sprintf("NAMING LOOKUP NAME=%s \n", name)
lookup := fmt.Sprintf("NAMING LOOKUP NAME=%s \n", name)
log.WithField("lookup", lookup).Debug("Generated NAMING LOOKUP command")
return lookup
}
func (e *SAMEmit) LookupBytes(name string) []byte {
@@ -44,32 +48,36 @@ func (e *SAMEmit) LookupBytes(name string) []byte {
}
func (e *SAMEmit) Create() string {
return fmt.Sprintf(
create := fmt.Sprintf(
// //1 2 3 4 5 6 7
"SESSION CREATE %s%s%s%s%s%s%s \n",
e.I2PConfig.SessionStyle(), //1
e.I2PConfig.FromPort(), //2
e.I2PConfig.ToPort(), //3
e.I2PConfig.ID(), //4
e.I2PConfig.DestinationKey(), // 5
e.I2PConfig.SignatureType(), // 6
e.OptStr(), // 7
e.I2PConfig.DestinationKey(), //5
e.I2PConfig.SignatureType(), //6
e.OptStr(), //7
)
log.WithField("create", create).Debug("Generated SESSION CREATE command")
return create
}
func (e *SAMEmit) CreateBytes() []byte {
log.Println("sam command: " + e.Create())
fmt.Println("sam command: " + e.Create())
return []byte(e.Create())
}
func (e *SAMEmit) Connect(dest string) string {
return fmt.Sprintf(
connect := fmt.Sprintf(
"STREAM CONNECT ID=%s %s %s DESTINATION=%s \n",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
dest,
)
log.WithField("connect", connect).Debug("Generated STREAM CONNECT command")
return connect
}
func (e *SAMEmit) ConnectBytes(dest string) []byte {
@@ -77,12 +85,14 @@ func (e *SAMEmit) ConnectBytes(dest string) []byte {
}
func (e *SAMEmit) Accept() string {
return fmt.Sprintf(
accept := fmt.Sprintf(
"STREAM ACCEPT ID=%s %s %s",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
)
log.WithField("accept", accept).Debug("Generated STREAM ACCEPT command")
return accept
}
func (e *SAMEmit) AcceptBytes() []byte {
@@ -93,9 +103,11 @@ func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
var emit SAMEmit
for _, o := range opts {
if err := o(&emit); err != nil {
log.WithError(err).Error("Failed to apply option")
return nil, err
}
}
log.Debug("New SAMEmit instance created")
return &emit, nil
}
@@ -104,6 +116,7 @@ func IgnorePortError(err error) error {
return nil
}
if strings.Contains(err.Error(), "missing port in address") {
log.Debug("Ignoring 'missing port in address' error")
err = nil
}
return err
@@ -113,10 +126,14 @@ func SplitHostPort(hostport string) (string, string, error) {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
if IgnorePortError(err) == nil {
log.Println("host: " + hostport)
log.WithField("host", hostport).Debug("Using full string as host, port set to 0")
host = hostport
port = "0"
}
}
log.WithFields(logrus.Fields{
"host": host,
"port": port,
}).Debug("Split host and port")
return host, port, nil
}

12
go.mod
View File

@@ -1,13 +1,9 @@
module github.com/eyedeekay/sam3
module github.com/go-i2p/sam3
go 1.12
require (
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae // indirect
github.com/google/renameio v1.0.0 // indirect
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33
github.com/rogpeppe/go-internal v1.6.2 // indirect
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b // indirect
honnef.co/go/tools v0.0.1-2020.1.6 // indirect
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4
github.com/sirupsen/logrus v1.9.3
golang.org/x/sys v0.27.0 // indirect
)

103
go.sum
View File

@@ -1,90 +1,19 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 h1:iPf1jQ8yKTms6k6L5vYSE7RZJpjEe5vLTOmzRZdpnKc=
github.com/cryptix/go v1.3.1 h1:I9opbROgEpldI0PwkMku0UY2DLFYgelZd9u0uaxmMgY=
github.com/cryptix/go v1.3.1/go.mod h1:mFQotm9rTzptzvNjJM+1vSIDa/rVOVqMu0889GIXg70=
github.com/cryptix/goSam v0.1.0 h1:lKXtrTv3Kd6+eIuNtcq3zPShJEVRqw+lQwmh49HmC7k=
github.com/cryptix/goSam v0.1.0/go.mod h1:7ewkjhXT8V5RG07pvWUOHHtMahvGbeKlEv8ukUyRiTA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eyedeekay/goSam v0.32.30 h1:mMlZNE2oISdjjjpgfN17W56tn9F8rD/Jc2tsjTDDFYg=
github.com/eyedeekay/goSam v0.32.30/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6 h1:seMFdfTWvmAsyj9jYPZATBJiJu5gHPsvJJk4Ruo2npQ=
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23 h1:AHm/EzBilSQH+RFgEuslnlCpVQd88MQWx7KHW/VIQlc=
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae h1:SwegHeaf4pkDMB24UltIvJlj2+nd06QUZAbs8BDyfjM=
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio v1.0.0 h1:xhp2CnJmgQmpJU4RY8chagahUq5mbPPAbiSQstKpVMA=
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0 h1:clkDYGefEWUCwyCrwYn900sOaVGDpinPJgD0W6ebEjs=
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0/go.mod h1:P6fDJzlxN+cWYR09KbE9/ta+Y6JofX9tAUhJpWkWPaM=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4 h1:LRjaRCzg1ieGKZjELlaIg06Fx04RHzQLsWMYp1H6PQ4=
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33 h1:dyyWDK0yzlZ8ay89Oe5ZIRtscacUjFyPUFGChrgMXRg=
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef h1:RHORRhs540cYZYrzgU2CPUyykkwZM78hGdzocOo9P8A=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -6,10 +6,13 @@ import (
"net"
"os"
"github.com/eyedeekay/i2pkeys"
"github.com/eyedeekay/sam3"
"github.com/go-i2p/i2pkeys"
"github.com/go-i2p/sam3"
)
// HEY! If you're looking at this, there's a good chance that `github.com/go-i2p/onramp`
// is a better fit! Check it out.
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
return I2PListener(name, sam3.SAMDefaultAddr(samaddr), keyspath)
}
@@ -20,7 +23,9 @@ func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
log.Printf("Starting and registering I2P service, please wait a couple of minutes...")
listener, err := I2PStreamSession(name, sam3.SAMDefaultAddr(samaddr), keyspath)
if err != nil {
return nil, err
}
if keyspath != "" {
err = ioutil.WriteFile(keyspath+".i2p.public.txt", []byte(listener.Keys().Addr().Base32()), 0644)
if err != nil {

View File

@@ -7,6 +7,7 @@
<meta name="description" content="sam3" />
<meta name="keywords" content="master" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" type="text/css" href="showhider.css" />
</head>
<body>
<div id="navbar">
@@ -30,19 +31,23 @@
</div>
</div>
<h1>
<a href="/">
README
<a href="#readme" rel="nofollow">
<span></span>
</a>
README
</h1>
<h2>
<a href="#important" rel="nofollow">
<span></span>
</a>
!!IMPORTANT!!
</h2>
<p>
In the next version, Ill be moving the
In the next version, I&#39;ll be moving the
<code>
i2pkeys
</code>
directory to its own repository
directory to it&#39;s own repository
so I can avoid import cycle headaches. Please migrate to the new
<code>
i2pkeys
@@ -50,9 +55,17 @@
repository
before upgrading your sam3 dependencies. You can probably do this by running:
</p>
<pre><code>find . -name &#39;*.go&#39; -exec sed -i &#39;s|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g&#39; {} \;
</code></pre>
<div>
<pre>find . -name &#39;*.go&#39; -exec sed -i &#39;s|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g&#39; {} \;
</pre>
</div>
<p>
STATUS: This project is maintained. I will respond to issues, pull requests, and feature requests within a few days.
</p>
<h1>
<a href="#readme" rel="nofollow">
<span></span>
</a>
README
</h1>
<p>
@@ -66,6 +79,9 @@
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
</p>
<h2>
<a href="#support-todo" rel="nofollow">
<span></span>
</a>
Support/TODO
</h2>
<p>
@@ -133,6 +149,9 @@
</li>
</ul>
<h2>
<a href="#documentation" rel="nofollow">
<span></span>
</a>
Documentation
</h2>
<ul>
@@ -160,9 +179,13 @@
</li>
</ul>
<h2>
<a href="#examples" rel="nofollow">
<span></span>
</a>
Examples
</h2>
<pre><code>package main
<div>
<pre>package main
import (
&#34;github.com/eyedeekay/sam3&#34;
@@ -193,17 +216,23 @@ func main() {
n, _ := conn.Read(buf)
fmt.Println(&#34;Server received: &#34; + string(buf[:n]))
}
</code></pre>
</pre>
</div>
<p>
The above will write to the terminal:
</p>
<pre><code>Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
<div>
<pre>Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
Server received: Hello world!
</code></pre>
</pre>
</div>
<p>
Error handling was omitted in the above code for readability.
</p>
<h2>
<a href="#testing" rel="nofollow">
<span></span>
</a>
Testing
</h2>
<ul>
@@ -221,12 +250,18 @@ Server received: Hello world!
</li>
</ul>
<h2>
<a href="#license" rel="nofollow">
<span></span>
</a>
License
</h2>
<p>
Public domain.
</p>
<h2>
<a href="#author" rel="nofollow">
<span></span>
</a>
Author
</h2>
<ul>

50
log.go Normal file
View File

@@ -0,0 +1,50 @@
package sam3
import (
"github.com/sirupsen/logrus"
"io/ioutil"
"os"
"strings"
"sync"
)
var (
log *logrus.Logger
once sync.Once
)
func InitializeSAM3Logger() {
once.Do(func() {
log = logrus.New()
// We do not want to log by default
log.SetOutput(ioutil.Discard)
log.SetLevel(logrus.PanicLevel)
// Check if DEBUG_I2P is set
if logLevel := os.Getenv("DEBUG_I2P"); logLevel != "" {
log.SetOutput(os.Stdout)
switch strings.ToLower(logLevel) {
case "debug":
log.SetLevel(logrus.DebugLevel)
case "warn":
log.SetLevel(logrus.WarnLevel)
case "error":
log.SetLevel(logrus.ErrorLevel)
default:
log.SetLevel(logrus.DebugLevel)
}
log.WithField("level", log.GetLevel()).Debug("Logging enabled.")
}
})
}
// GetSAM3Logger returns the initialized logger
func GetSAM3Logger() *logrus.Logger {
if log == nil {
InitializeSAM3Logger()
}
return log
}
func init() {
InitializeSAM3Logger()
}

View File

@@ -3,13 +3,14 @@ package sam3
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
"math/rand"
"net"
"strconv"
"strings"
"time"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
const (
@@ -20,6 +21,8 @@ func randport() string {
s := rand.NewSource(time.Now().UnixNano())
r := rand.New(s)
p := r.Intn(55534) + 10000
port := strconv.Itoa(p)
log.WithField("port", port).Debug("Generated random port")
return strconv.Itoa(p)
}
@@ -76,6 +79,7 @@ func (ss *PrimarySession) Keys() i2pkeys.I2PKeys {
}
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "addr": addr}).Debug("Dial() called")
if network == "udp" || network == "udp4" || network == "udp6" {
//return sam.DialUDPI2P(network, network+addr[0:4], addr)
return sam.DialUDPI2P(network, network+addr[0:4], addr)
@@ -84,16 +88,19 @@ func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
//return sam.DialTCPI2P(network, network+addr[0:4], addr)
return sam.DialTCPI2P(network, network+addr[0:4], addr)
}
log.WithField("network", network).Error("Invalid network type")
return nil, fmt.Errorf("Error: Must specify a valid network type")
}
// DialTCP implements x/dialer
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialTCP() called")
ts, ok := sam.stsess[network+raddr.String()[0:4]]
var err error
if !ok {
ts, err = sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
if err != nil {
log.WithError(err).Error("Failed to create new unique stream sub-session")
return nil, err
}
sam.stsess[network+raddr.String()[0:4]] = ts
@@ -103,11 +110,13 @@ func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.C
}
func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialTCPI2P() called")
ts, ok := sam.stsess[network+raddr[0:4]]
var err error
if !ok {
ts, err = sam.NewUniqueStreamSubSession(network + laddr)
if err != nil {
log.WithError(err).Error("Failed to create new unique stream sub-session")
return nil, err
}
sam.stsess[network+raddr[0:4]] = ts
@@ -118,11 +127,13 @@ func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (net.
// DialUDP implements x/dialer
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialUDP() called")
ds, ok := sam.dgsess[network+raddr.String()[0:4]]
var err error
if !ok {
ds, err = sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
if err != nil {
log.WithError(err).Error("Failed to create new datagram sub-session")
return nil, err
}
sam.dgsess[network+raddr.String()[0:4]] = ds
@@ -132,11 +143,13 @@ func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.P
}
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSession, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialUDPI2P() called")
ds, ok := sam.dgsess[network+raddr[0:4]]
var err error
if !ok {
ds, err = sam.NewDatagramSubSession(network+laddr, 0)
if err != nil {
log.WithError(err).Error("Failed to create new datagram sub-session")
return nil, err
}
sam.dgsess[network+raddr[0:4]] = ds
@@ -146,33 +159,51 @@ func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSe
}
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
log.WithField("name", name).Debug("Lookup() called")
var sam *SAM
name = strings.Split(name, ":")[0]
sam, err = NewSAM(s.samAddr)
if err == nil {
log.WithField("addr", a).Debug("Lookup successful")
defer sam.Close()
a, err = sam.Lookup(name)
}
log.WithError(err).Error("Lookup failed")
return
}
func (sam *PrimarySession) Resolve(network, addr string) (net.Addr, error) {
log.WithFields(logrus.Fields{"network": network, "addr": addr}).Debug("Resolve() called")
return sam.Lookup(addr)
}
func (sam *PrimarySession) ResolveTCPAddr(network, dest string) (net.Addr, error) {
log.WithFields(logrus.Fields{"network": network, "dest": dest}).Debug("ResolveTCPAddr() called")
return sam.Lookup(dest)
}
func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error) {
log.WithFields(logrus.Fields{"network": network, "dest": dest}).Debug("ResolveUDPAddr() called")
return sam.Lookup(dest)
}
// Creates a new PrimarySession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
conn, err := sam.newGenericSession(PrimarySessionSwitch, id, keys, options, []string{})
log.WithFields(logrus.Fields{"id": id, "options": options}).Debug("NewPrimarySession() called")
return sam.newPrimarySession(PrimarySessionSwitch, id, keys, options)
}
func (sam *SAM) newPrimarySession(primarySessionSwitch string, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
log.WithFields(logrus.Fields{
"primarySessionSwitch": primarySessionSwitch,
"id": id,
"options": options,
}).Debug("newPrimarySession() called")
conn, err := sam.newGenericSession(primarySessionSwitch, id, keys, options, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic session")
return nil, err
}
ssesss := make(map[string]*StreamSession)
@@ -183,8 +214,15 @@ func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []str
// Creates a new PrimarySession with the I2CP- and PRIMARYinglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*PrimarySession, error) {
log.WithFields(logrus.Fields{
"id": id,
"options": options,
"sigType": sigType,
}).Debug("NewPrimarySessionWithSignature() called")
conn, err := sam.newGenericSessionWithSignature(PrimarySessionSwitch, id, keys, sigType, options, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic session with signature")
return nil, err
}
ssesss := make(map[string]*StreamSession)
@@ -198,10 +236,12 @@ func (sam *SAM) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys,
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) newGenericSubSession(style, id string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "extras": extras}).Debug("newGenericSubSession called")
return sam.newGenericSubSessionWithSignature(style, id, extras)
}
func (sam *PrimarySession) newGenericSubSessionWithSignature(style, id string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "extras": extras}).Debug("newGenericSubSessionWithSignature called")
return sam.newGenericSubSessionWithSignatureAndPorts(style, id, "0", "0", extras)
}
@@ -211,6 +251,7 @@ func (sam *PrimarySession) newGenericSubSessionWithSignature(style, id string, e
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id, from, to string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "extras": extras}).Debug("newGenericSubSessionWithSignatureAndPorts called")
conn := sam.conn
fp := ""
@@ -222,13 +263,18 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION ADD STYLE=" + style + " ID=" + id + fp + tp + " " + strings.Join(extras, " ") + "\n")
log.WithField("message", string(scmsg)).Debug("Sending SESSION ADD message")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
log.Error("Writing to SAM failed after 15 attempts")
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
conn.Close()
return nil, err
}
@@ -237,30 +283,38 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read from SAM connection")
conn.Close()
return nil, err
}
text := string(buf[:n])
log.WithField("response", text).Debug("Received response from SAM")
//log.Println("SAM:", text)
if strings.HasPrefix(text, session_ADDOK) {
//if sam.keys.String() != text[len(session_ADDOK):len(text)-1] {
//conn.Close()
//return nil, errors.New("SAMv3 created a tunnel with keys other than the ones we asked it for")
//}
log.Debug("Session added successfully")
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == session_DUPLICATE_ID {
log.Error("Duplicate tunnel name")
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == session_DUPLICATE_DEST {
log.Error("Duplicate destination")
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
log.Error("Invalid key - Primary Session")
conn.Close()
return nil, errors.New("Invalid key - Primary Session")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
log.WithField("error", text[len(session_I2P_ERROR):]).Error("I2P error")
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
} else {
log.WithField("reply", text).Error("Unable to parse SAMv3 reply")
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
@@ -269,8 +323,10 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error) {
log.WithField("id", id).Debug("NewStreamSubSession called")
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
@@ -279,18 +335,25 @@ func (sam *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewUniqueStreamSubSession(id string) (*StreamSession, error) {
log.WithField("id", id).Debug("NewUniqueStreamSubSession called")
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), randport()}, nil
fromPort, toPort := randport(), randport()
log.WithFields(logrus.Fields{"fromPort": fromPort, "toPort": toPort}).Debug("Generated random ports")
//return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), randport()}, nil
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, fromPort, toPort}, nil
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSessionWithPorts(id, from, to string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to}).Debug("NewStreamSubSessionWithPorts called")
conn, err := sam.newGenericSubSessionWithSignatureAndPorts("STREAM", id, from, to, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session with signature and ports")
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, from, to}, nil
@@ -309,86 +372,111 @@ func (s *PrimarySession) I2PListener(name string) (*StreamListener, error) {
// 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 *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*DatagramSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("NewDatagramSubSession called")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
conn, err := s.newGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "localPort": lport}).Debug("Created new datagram sub-session")
return &DatagramSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, s.keys, rUDPAddr, nil}, nil
}
// 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 *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("NewRawSubSession called")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
conn, err := s.newGenericSubSession("RAW", id, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "localPort": lport}).Debug("Created new raw sub-session")
return &RawSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, s.keys, rUDPAddr}, nil
}

View File

@@ -2,7 +2,6 @@ package sam3
import (
"fmt"
"log"
"testing"
"time"
)
@@ -140,7 +139,7 @@ func ExamplePrimaryDatagramSession() {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return

View File

@@ -3,7 +3,6 @@ package sam3
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"testing"

53
raw.go
View File

@@ -3,11 +3,12 @@ package sam3
import (
"bytes"
"errors"
"github.com/sirupsen/logrus"
"net"
"strconv"
"time"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
// The RawSession provides no authentication of senders, and there is no sender
@@ -28,39 +29,59 @@ type RawSession struct {
// 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.I2PKeys, options []string, udpPort int) (*RawSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("Creating new RawSession")
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the interval 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.Debug("Using default UDP port 7655")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
conn, err := s.newGenericSession("RAW", id, keys, options, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to get local port")
return nil, err
}
conn, err := s.newGenericSession("RAW", id, keys, options, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic session")
return nil, err
}
log.WithFields(logrus.Fields{
"id": id,
"localPort": lport,
"remoteUDPAddr": rUDPAddr,
}).Debug("Created new RawSession")
return &RawSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, keys, rUDPAddr}, nil
}
@@ -68,37 +89,61 @@ func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, u
// the number of bytes read. Who sent the raw message can not be determined at
// this layer - you need to do it (in a secure way!).
func (s *RawSession) Read(b []byte) (n int, err error) {
log.Debug("Attempting to read raw datagram")
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.udpconn.ReadFromUDP(b)
if err != nil {
log.WithError(err).Error("Failed to read from UDP")
return 0, err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
log.WithField("senderIP", saddr.IP).Debug("Received datagram from SAM bridge IP")
continue
}
break
}
log.WithField("bytesRead", n).Debug("Successfully read raw datagram")
return n, nil
}
// 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 i2pkeys.I2PAddr) (n int, err error) {
log.WithFields(logrus.Fields{
"destAddr": addr.String(),
"dataLen": len(b),
}).Debug("Attempting to write raw datagram")
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to write to UDP")
}
log.WithField("bytesWritten", n).Debug("Successfully wrote raw datagram")
return n, err
}
// Closes the RawSession.
func (s *RawSession) Close() error {
log.Debug("Closing RawSession")
err := s.conn.Close()
err2 := s.udpconn.Close()
if err != nil {
log.WithError(err).Error("Failed to close connection")
return err
}
err2 := s.udpconn.Close()
if err2 != nil {
log.WithError(err2).Error("Failed to close UDP connection")
}
log.Debug("RawSession closed")
return err2
}

View File

@@ -6,7 +6,7 @@ import (
"errors"
"strings"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
type SAMResolver struct {
@@ -14,16 +14,19 @@ type SAMResolver struct {
}
func NewSAMResolver(parent *SAM) (*SAMResolver, error) {
log.Debug("Creating new SAMResolver from existing SAM instance")
var s SAMResolver
s.SAM = parent
return &s, nil
}
func NewFullSAMResolver(address string) (*SAMResolver, error) {
log.WithField("address", address).Debug("Creating new full SAMResolver")
var s SAMResolver
var err error
s.SAM, err = NewSAM(address)
if err != nil {
log.WithError(err).Error("Failed to create new SAM instance")
return nil, err
}
return &s, nil
@@ -32,17 +35,22 @@ 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) (i2pkeys.I2PAddr, error) {
log.WithField("name", name).Debug("Resolving name")
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\r\n")); err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
sam.Close()
return i2pkeys.I2PAddr(""), err
}
buf := make([]byte, 4096)
n, err := sam.conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read from SAM connection")
sam.Close()
return i2pkeys.I2PAddr(""), err
}
if n <= 13 || !strings.HasPrefix(string(buf[:n]), "NAMING REPLY ") {
log.Error("Failed to parse SAM response")
return i2pkeys.I2PAddr(""), errors.New("Failed to parse.")
}
s := bufio.NewScanner(bytes.NewReader(buf[13:n]))
@@ -51,19 +59,25 @@ func (sam *SAMResolver) Resolve(name string) (i2pkeys.I2PAddr, error) {
errStr := ""
for s.Scan() {
text := s.Text()
log.WithField("text", text).Debug("Parsing SAM response token")
//log.Println("SAM3", text)
if text == "RESULT=OK" {
continue
} else if text == "RESULT=INVALID_KEY" {
errStr += "Invalid key - resolver."
log.Error("Invalid key in resolver")
} else if text == "RESULT=KEY_NOT_FOUND" {
errStr += "Unable to resolve " + name
log.WithField("name", name).Error("Unable to resolve name")
} else if text == "NAME="+name {
continue
} else if strings.HasPrefix(text, "VALUE=") {
addr := i2pkeys.I2PAddr(text[6:])
log.WithField("addr", addr).Debug("Name resolved successfully")
return i2pkeys.I2PAddr(text[6:]), nil
} else if strings.HasPrefix(text, "MESSAGE=") {
errStr += " " + text[8:]
log.WithField("message", text[8:]).Warn("Received message from SAM")
} else {
continue
}

82
sam3.go
View File

@@ -5,17 +5,23 @@ import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"io"
"math/rand"
"net"
"os"
"strings"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
. "github.com/eyedeekay/i2pkeys"
. "github.com/go-i2p/i2pkeys"
)
func init() {
InitializeSAM3Logger()
}
// Used for controlling I2Ps SAMv3.
type SAM struct {
address string
@@ -35,7 +41,7 @@ const (
)
const (
Sig_NONE = ""
Sig_NONE = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
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"
@@ -51,41 +57,49 @@ func RandString() string {
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
log.WithField("randomString", string(b)).Debug("Generated random string")
return string(b)
}
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
log.WithField("address", address).Debug("Creating new SAM instance")
var s SAM
// TODO: clean this up
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
log.WithError(err).Error("Failed to dial SAM address")
return nil, fmt.Errorf("error dialing to address '%s': %w", address, err)
}
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
log.WithError(err).Error("Failed to write hello message")
conn.Close()
return nil, err
return nil, fmt.Errorf("error writing to address '%s': %w", address, err)
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read SAM response")
conn.Close()
return nil, err
return nil, fmt.Errorf("error reading onto buffer: %w", err)
}
if strings.Contains(string(buf[:n]), "HELLO REPLY RESULT=OK") {
log.Debug("SAM hello successful")
s.Config.I2PConfig.SetSAMAddress(address)
s.conn = conn
//s.Config.I2PConfig.DestinationKeys = nil
s.resolver, err = NewSAMResolver(&s)
if err != nil {
return nil, err
log.WithError(err).Error("Failed to create SAM resolver")
return nil, fmt.Errorf("error creating resolver: %w", err)
}
return &s, nil
//return &SAM{address, conn, nil, nil}, nil
} else if string(buf[:n]) == "HELLO REPLY RESULT=NOVERSION\n" {
log.Error("SAM bridge does not support SAMv3")
conn.Close()
return nil, errors.New("That SAM bridge does not support SAMv3.")
} else {
log.WithField("response", string(buf[:n])).Error("Unexpected SAM response")
conn.Close()
return nil, errors.New(string(buf[:n]))
}
@@ -93,30 +107,38 @@ func NewSAM(address string) (*SAM, error) {
func (sam *SAM) Keys() (k *i2pkeys.I2PKeys) {
//TODO: copy them?
log.Debug("Retrieving SAM keys")
k = &sam.Config.I2PConfig.DestinationKeys
return
}
// read public/private keys from an io.Reader
func (sam *SAM) ReadKeys(r io.Reader) (err error) {
log.Debug("Reading keys from io.Reader")
var keys i2pkeys.I2PKeys
keys, err = i2pkeys.LoadKeysIncompat(r)
if err == nil {
log.Debug("Keys loaded successfully")
sam.Config.I2PConfig.DestinationKeys = keys
}
log.WithError(err).Error("Failed to load keys")
return
}
// if keyfile fname does not exist
func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
log.WithError(err).Error("Failed to load keys")
if fname == "" {
// transient
keys, err = sam.NewKeys()
if err == nil {
sam.Config.I2PConfig.DestinationKeys = keys
log.WithFields(logrus.Fields{
"keys": keys,
}).Debug("Generated new transient keys")
}
} else {
// persistant
// persistent
_, err = os.Stat(fname)
if os.IsNotExist(err) {
// make the keys
@@ -129,6 +151,7 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
if err == nil {
err = i2pkeys.StoreKeysIncompat(keys, f)
f.Close()
log.Debug("Generated and saved new keys")
}
}
} else if err == nil {
@@ -139,10 +162,14 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
keys, err = i2pkeys.LoadKeysIncompat(f)
if err == nil {
sam.Config.I2PConfig.DestinationKeys = keys
log.Debug("Loaded existing keys from file")
}
}
}
}
if err != nil {
log.WithError(err).Error("Failed to ensure keyfile")
}
return
}
@@ -150,17 +177,20 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
// 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.I2PKeys, error) {
log.WithField("sigType", sigType).Debug("Generating new keys")
sigtmp := ""
if len(sigType) > 0 {
sigtmp = sigType[0]
}
if _, err := sam.conn.Write([]byte("DEST GENERATE " + sigtmp + "\n")); err != nil {
return i2pkeys.I2PKeys{}, err
log.WithError(err).Error("Failed to write DEST GENERATE command")
return i2pkeys.I2PKeys{}, fmt.Errorf("error with writing in SAM: %w", err)
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return i2pkeys.I2PKeys{}, err
log.WithError(err).Error("Failed to read SAM response for key generation")
return i2pkeys.I2PKeys{}, fmt.Errorf("error with reading in SAM: %w", err)
}
s := bufio.NewScanner(bytes.NewReader(buf[:n]))
s.Split(bufio.ScanWords)
@@ -177,15 +207,18 @@ func (sam *SAM) NewKeys(sigType ...string) (i2pkeys.I2PKeys, error) {
} else if strings.HasPrefix(text, "PRIV=") {
priv = text[5:]
} else {
log.Error("Failed to parse keys from SAM response")
return i2pkeys.I2PKeys{}, errors.New("Failed to parse keys.")
}
}
log.Debug("Successfully generated new keys")
return NewKeys(I2PAddr(pub), priv), nil
}
// 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) {
log.WithField("name", name).Debug("Looking up address")
return sam.resolver.Resolve(name)
}
@@ -195,10 +228,12 @@ func (sam *SAM) Lookup(name string) (i2pkeys.I2PAddr, error) {
// 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) {
log.WithFields(logrus.Fields{"style": style, "id": id}).Debug("Creating new generic session")
return sam.newGenericSessionWithSignature(style, id, keys, Sig_NONE, options, extras)
}
func (sam *SAM) newGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "sigType": sigType}).Debug("Creating new generic session with signature")
return sam.newGenericSessionWithSignatureAndPorts(style, id, "0", "0", keys, sigType, options, extras)
}
@@ -208,11 +243,9 @@ func (sam *SAM) newGenericSessionWithSignature(style, id string, keys i2pkeys.I2
// 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) {
log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "sigType": sigType}).Debug("Creating new generic session with signature and ports")
optStr := ""
for _, opt := range options {
optStr += opt + " "
}
optStr := GenerateOptionString(options)
conn := sam.conn
fp := ""
@@ -224,44 +257,58 @@ func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to strin
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION CREATE STYLE=" + style + fp + tp + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + strings.Join(extras, " ") + "\n")
log.WithField("message", string(scmsg)).Debug("Sending SESSION CREATE message")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
log.Error("Failed to write SESSION CREATE message after 15 attempts")
conn.Close()
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
conn.Close()
return nil, err
return nil, fmt.Errorf("writing to connection failed: %w", err)
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read SAM response")
conn.Close()
return nil, err
return nil, fmt.Errorf("reading from connection failed: %w", err)
}
text := string(buf[:n])
log.WithField("response", text).Debug("Received SAM response")
if strings.HasPrefix(text, session_OK) {
if keys.String() != text[len(session_OK):len(text)-1] {
log.Error("SAM created a tunnel with different keys than requested")
conn.Close()
return nil, errors.New("SAMv3 created a tunnel with keys other than the ones we asked it for")
}
log.Debug("Successfully created new session")
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == session_DUPLICATE_ID {
log.Error("Duplicate tunnel name")
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == session_DUPLICATE_DEST {
log.Error("Duplicate destination")
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
log.Error("Invalid key for SAM session")
conn.Close()
return nil, errors.New("Invalid key - SAM session")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
log.WithField("error", text[len(session_I2P_ERROR):]).Error("I2P error")
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
} else {
log.WithField("reply", text).Error("Unable to parse SAMv3 reply")
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
@@ -269,5 +316,6 @@ func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to strin
// close this sam session
func (sam *SAM) Close() error {
log.Debug("Closing SAM session")
return sam.conn.Close()
}

10
showhider.css Normal file
View File

@@ -0,0 +1,10 @@
/* edgar showhider CSS file */
#show {display:none; }
#hide {display:block; }
#show:target {display: block; }
#hide:target {display: none; }
#shownav {display:none; }
#hidenav {display:block; }
#shownav:target {display: block; }
#hidenav:target {display: none; }

View File

@@ -5,12 +5,13 @@ import (
"bytes"
"context"
"errors"
"github.com/sirupsen/logrus"
"io"
"net"
"strings"
"time"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
// Represents a streaming session.
@@ -26,102 +27,132 @@ type StreamSession struct {
to string
}
// Read reads data from the stream.
func (s *StreamSession) Read(buf []byte) (int, error) {
return s.conn.Read(buf)
}
// Write sends data over the stream.
func (s *StreamSession) Write(data []byte) (int, error) {
return s.conn.Write(data)
}
func (s *StreamSession) SetDeadline(t time.Time) error {
log.WithField("deadline", t).Debug("Setting deadline for StreamSession")
return s.conn.SetDeadline(t)
}
func (s *StreamSession) SetReadDeadline(t time.Time) error {
log.WithField("readDeadline", t).Debug("Setting read deadline for StreamSession")
return s.conn.SetReadDeadline(t)
}
func (s *StreamSession) SetWriteDeadline(t time.Time) error {
log.WithField("writeDeadline", t).Debug("Setting write deadline for StreamSession")
return s.conn.SetWriteDeadline(t)
}
func (ss *StreamSession) From() string {
return ss.from
func (s *StreamSession) From() string {
return s.from
}
func (ss *StreamSession) To() string {
return ss.to
func (s *StreamSession) To() string {
return s.to
}
func (ss *StreamSession) SignatureType() string {
return ss.sigType
func (s *StreamSession) SignatureType() string {
return s.sigType
}
// Returns the local tunnel name of the I2P tunnel used for the stream session
func (ss *StreamSession) ID() string {
return ss.id
func (s *StreamSession) ID() string {
return s.id
}
func (ss *StreamSession) Close() error {
return ss.conn.Close()
func (s *StreamSession) Close() error {
log.WithField("id", s.id).Debug("Closing StreamSession")
return s.conn.Close()
}
// Returns the I2P destination (the address) of the stream session
func (ss *StreamSession) Addr() i2pkeys.I2PAddr {
return ss.keys.Addr()
func (s *StreamSession) Addr() i2pkeys.I2PAddr {
return s.keys.Addr()
}
func (ss *StreamSession) LocalAddr() net.Addr {
return ss.keys.Addr()
func (s *StreamSession) LocalAddr() net.Addr {
return s.keys.Addr()
}
// Returns the keys associated with the stream session
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
return ss.keys
func (s *StreamSession) Keys() i2pkeys.I2PKeys {
return s.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.I2PKeys, options []string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "options": options}).Debug("Creating new StreamSession")
conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
if err != nil {
return nil, err
}
log.WithField("id", id).Debug("Created new StreamSession")
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
}
// 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.I2PKeys, options []string, sigType string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "options": options, "sigType": sigType}).Debug("Creating new StreamSession with signature")
conn, err := sam.newGenericSessionWithSignature("STREAM", id, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "sigType": sigType}).Debug("Created new StreamSession with signature")
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, "0", "0"}, nil
}
// 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.I2PKeys, options []string, sigType string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to, "options": options, "sigType": sigType}).Debug("Creating new StreamSession with signature and ports")
conn, err := sam.newGenericSessionWithSignatureAndPorts("STREAM", id, from, to, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to, "sigType": sigType}).Debug("Created new StreamSession with signature and ports")
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, from, to}, nil
}
// lookup name, convienence function
// lookup name, convenience function
func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
log.WithField("name", name).Debug("Looking up address")
sam, err := NewSAM(s.samAddr)
if err == nil {
addr, err := sam.Lookup(name)
defer sam.Close()
if err != nil {
log.WithError(err).Error("Lookup failed")
} else {
log.WithField("addr", addr).Debug("Lookup successful")
}
return addr, err
}
log.WithError(err).Error("Failed to create SAM instance for lookup")
return i2pkeys.I2PAddr(""), err
}
// context-aware dialer, eventually...
func (s *StreamSession) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": n, "addr": addr}).Debug("DialContext called")
return s.DialContextI2P(ctx, n, addr)
}
// context-aware dialer, eventually...
func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SAMConn, error) {
log.WithFields(logrus.Fields{"network": n, "addr": addr}).Debug("DialContextI2P called")
if ctx == nil {
log.Panic("nil context")
panic("nil context")
}
deadline := s.deadline(ctx, time.Now())
@@ -135,6 +166,7 @@ func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SA
i2paddr, err := i2pkeys.NewI2PAddrFromString(addr)
if err != nil {
log.WithError(err).Error("Failed to create I2P address from string")
return nil, err
}
return s.DialI2P(i2paddr)
@@ -161,6 +193,7 @@ func minNonzeroTime(a, b time.Time) time.Time {
// - 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
@@ -174,6 +207,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) {
log.WithFields(logrus.Fields{"network": n, "addr": addr}).Debug("Dial called")
var i2paddr i2pkeys.I2PAddr
var host string
@@ -184,35 +218,41 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
// name lookup
i2paddr, err = s.Lookup(host)
//log.Println("Lookup:", i2paddr, err)
log.WithFields(logrus.Fields{"host": host, "i2paddr": i2paddr}).Debug("Looked up I2P address")
} else {
// probably a destination
i2paddr, err = i2pkeys.NewI2PAddrFromBytes([]byte(host))
//i2paddr = i2pkeys.I2PAddr(host)
//log.Println("Destination:", i2paddr, err)
log.WithFields(logrus.Fields{"host": host, "i2paddr": i2paddr}).Debug("Created I2P address from bytes")
}
if err == nil {
return s.DialI2P(i2paddr)
}
}
log.WithError(err).Error("Dial failed")
return
}
// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
log.WithField("addr", addr).Debug("DialI2P called")
sam, err := NewSAM(s.samAddr)
if err != nil {
log.WithError(err).Error("Failed to create new SAM instance")
return nil, err
}
conn := sam.conn
_, err = conn.Write([]byte("STREAM CONNECT ID=" + s.id + " FROM_PORT=" + s.from + " TO_PORT=" + s.to + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
if err != nil {
log.WithError(err).Error("Failed to write STREAM CONNECT command")
conn.Close()
return nil, err
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil && err != io.EOF {
log.WithError(err).Error("Failed to write STREAM CONNECT command")
conn.Close()
return nil, err
}
@@ -225,32 +265,41 @@ func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
case "STATUS":
continue
case "RESULT=OK":
log.Debug("Successfully connected to I2P destination")
return &SAMConn{s.keys.Addr(), addr, conn}, nil
case "RESULT=CANT_REACH_PEER":
log.Error("Can't reach peer")
conn.Close()
return nil, errors.New("Can not reach peer")
case "RESULT=I2P_ERROR":
log.Error("I2P internal error")
conn.Close()
return nil, errors.New("I2P internal error")
case "RESULT=INVALID_KEY":
log.Error("Invalid key - Stream Session")
conn.Close()
return nil, errors.New("Invalid key - Stream Session")
case "RESULT=INVALID_ID":
log.Error("Invalid tunnel ID")
conn.Close()
return nil, errors.New("Invalid tunnel ID")
case "RESULT=TIMEOUT":
log.Error("Connection timeout")
conn.Close()
return nil, errors.New("Timeout")
default:
log.WithField("error", scanner.Text()).Error("Unknown error")
conn.Close()
return nil, errors.New("Unknown error: " + scanner.Text() + " : " + string(buf[:n]))
}
}
log.Panic("Unexpected end of StreamSession.DialI2P()")
panic("sam3 go library error in StreamSession.DialI2P()")
}
// create a new stream listener to accept inbound connections
func (s *StreamSession) Listen() (*StreamListener, error) {
log.WithFields(logrus.Fields{"id": s.id, "laddr": s.keys.Addr()}).Debug("Creating new StreamListener")
return &StreamListener{
session: s,
id: s.id,

View File

@@ -3,13 +3,13 @@ package sam3
import (
"bufio"
"errors"
"github.com/sirupsen/logrus"
"io"
"log"
"net"
"strconv"
"strings"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
type StreamListener struct {
@@ -46,70 +46,95 @@ func (l *StreamListener) Accept() (net.Conn, error) {
}
func ExtractPairString(input, value string) string {
log.WithFields(logrus.Fields{"input": input, "value": value}).Debug("ExtractPairString called")
parts := strings.Split(input, " ")
for _, part := range parts {
if strings.HasPrefix(part, value) {
kv := strings.SplitN(input, "=", 2)
if len(kv) == 2 {
log.WithFields(logrus.Fields{"key": kv[0], "value": kv[1]}).Debug("Pair extracted")
return kv[1]
}
}
}
log.WithFields(logrus.Fields{"input": input, "value": value}).Debug("No pair found")
return ""
}
func ExtractPairInt(input, value string) int {
rv, err := strconv.Atoi(ExtractPairString(input, value))
if err != nil {
log.WithFields(logrus.Fields{"input": input, "value": value}).Debug("No pair found")
return 0
}
log.WithField("result", rv).Debug("Pair extracted and converted to int")
return rv
}
func ExtractDest(input string) string {
log.WithField("input", input).Debug("ExtractDest called")
dest := strings.Split(input, " ")[0]
log.WithField("dest", dest).Debug("Destination extracted")
return strings.Split(input, " ")[0]
}
// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
log.Debug("StreamListener.AcceptI2P() called")
s, err := NewSAM(l.session.samAddr)
if err == nil {
log.Debug("Connected to SAM bridge")
// we connected to sam
// send accept() command
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
if err != nil {
log.WithError(err).Error("Failed to send STREAM ACCEPT command")
s.Close()
return nil, err
}
// read reply
rd := bufio.NewReader(s.conn)
// read first line
line, err := rd.ReadString(10)
log.Println(line)
if err == nil {
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
destline, err := rd.ReadString(10)
if err == nil {
dest := ExtractDest(destline)
l.session.from = ExtractPairString(destline, "FROM_PORT")
l.session.to = ExtractPairString(destline, "TO_PORT")
// return wrapped connection
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {
s.Close()
return nil, err
}
} else {
s.Close()
return nil, errors.New("invalid sam line: " + line)
}
} else {
if err != nil {
log.WithError(err).Error("Failed to read SAM bridge response")
s.Close()
return nil, err
}
log.WithField("response", line).Debug("Received SAM bridge response")
log.Println(line)
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
destline, err := rd.ReadString(10)
if err == nil {
dest := ExtractDest(destline)
l.session.from = ExtractPairString(destline, "FROM_PORT")
l.session.to = ExtractPairString(destline, "TO_PORT")
// return wrapped connection
dest = strings.Trim(dest, "\n")
log.WithFields(logrus.Fields{
"dest": dest,
"from": l.session.from,
"to": l.session.to,
}).Debug("Accepted new I2P connection")
return &SAMConn{
laddr: l.laddr,
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {
log.WithError(err).Error("Failed to read destination line")
s.Close()
return nil, err
}
} else {
log.WithField("line", line).Error("Invalid SAM response")
s.Close()
return nil, errors.New("invalid sam line: " + line)
}
} else {
log.WithError(err).Error("Failed to connect to SAM bridge")
s.Close()
return nil, err
}
s.Close()
return nil, err
}

View File

@@ -2,11 +2,10 @@ package sam3
import (
"fmt"
"log"
"strings"
"testing"
"github.com/eyedeekay/i2pkeys"
"github.com/go-i2p/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {

View File

@@ -1,9 +1,11 @@
package sam3
import (
"github.com/sirupsen/logrus"
"net"
"net/http"
"os"
"strings"
)
// Examples and suggestions for options when creating sessions.
@@ -59,24 +61,57 @@ var (
)
func PrimarySessionString() string {
log.Debug("Determining primary session type")
_, err := http.Get("http://127.0.0.1:7070")
if err != nil {
log.WithError(err).Debug("Failed to connect to 127.0.0.1:7070, trying 127.0.0.1:7657")
_, err := http.Get("http://127.0.0.1:7657")
if err != nil {
return "MASTER"
}
log.Debug("Connected to 127.0.0.1:7657, attempting to create a PRIMARY session")
// at this point we're probably running on Java I2P and thus probably
// have a PRIMARY session. Just to be sure, try to make one, check
// for errors, then immediately close it.
testSam, err := NewSAM(SAMDefaultAddr(""))
if err != nil {
log.WithError(err).Debug("Failed to create SAM instance, assuming MASTER session")
return "MASTER"
}
newKeys, err := testSam.NewKeys()
if err != nil {
log.WithError(err).Debug("Failed to create new keys, assuming MASTER session")
return "MASTER"
}
primarySession, err := testSam.newPrimarySession("PRIMARY", "primaryTestTunnel", newKeys, Options_Small)
if err != nil {
log.WithError(err).Debug("Failed to create primary session, assuming MASTER session")
return "MASTER"
}
primarySession.Close()
log.Debug("Successfully created and closed a PRIMARY session")
return "PRIMARY"
}
log.Debug("Connected to 127.0.0.1:7070, assuming MASTER session")
return "MASTER"
}
var PrimarySessionSwitch string = PrimarySessionString()
func getEnv(key, fallback string) string {
InitializeSAM3Logger()
value, ok := os.LookupEnv(key)
if !ok {
log.WithFields(logrus.Fields{
"key": key,
"fallback": fallback,
}).Debug("Environment variable not set, using fallback")
return fallback
}
log.WithFields(logrus.Fields{
"key": key,
"value": value,
}).Debug("Retrieved environment variable")
return value
}
@@ -85,7 +120,23 @@ var SAM_PORT = getEnv("sam_port", "7656")
func SAMDefaultAddr(fallforward string) string {
if fallforward == "" {
return net.JoinHostPort(SAM_HOST, SAM_PORT)
addr := net.JoinHostPort(SAM_HOST, SAM_PORT)
log.WithField("addr", addr).Debug("Using default SAM address")
return addr
}
log.WithField("addr", fallforward).Debug("Using fallforward SAM address")
return fallforward
}
func GenerateOptionString(opts []string) string {
optStr := strings.Join(opts, " ")
log.WithField("options", optStr).Debug("Generating option string")
if strings.Contains(optStr, "i2cp.leaseSetEncType") {
log.Debug("i2cp.leaseSetEncType already present in options")
return optStr
}
finalOpts := optStr + " i2cp.leaseSetEncType=4,0"
log.WithField("finalOptions", finalOpts).Debug("Added default i2cp.leaseSetEncType to options")
return finalOpts
//return optStr + " i2cp.leaseSetEncType=4,0"
}