package common import ( "strings" "github.com/go-i2p/i2pkeys" "github.com/samber/oops" "github.com/sirupsen/logrus" ) // Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW", // for a new I2P tunnel with name id, using the cypher keys specified, with the // I2CP/streaminglib-options as specified. Extra arguments can be specified by // setting extra to something else than []string{}. // This sam3 instance is now a session func (sam SAM) NewGenericSession(style, id string, keys i2pkeys.I2PKeys, extras []string) (Session, error) { log.WithFields(logrus.Fields{"style": style, "id": id}).Debug("Creating new generic session") return sam.NewGenericSessionWithSignature(style, id, keys, SIG_EdDSA_SHA512_Ed25519, extras) } func (sam SAM) NewGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, extras []string) (Session, 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, extras) } // Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW", // for a new I2P tunnel with name id, using the cypher keys specified, with the // I2CP/streaminglib-options as specified. Extra arguments can be specified by // setting extra to something else than []string{}. // This sam3 instance is now a session func (sam SAM) NewGenericSessionWithSignatureAndPorts(style, id, from, to string, keys i2pkeys.I2PKeys, sigType string, extras []string) (Session, error) { log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "sigType": sigType}).Debug("Creating new generic session with signature and ports") // Configure SAMEmit with all session parameters for message generation sam.SAMEmit.I2PConfig.Style = style sam.SAMEmit.I2PConfig.TunName = id sam.SAMEmit.I2PConfig.DestinationKeys = &keys sam.SAMEmit.I2PConfig.SigType = sigType sam.SAMEmit.I2PConfig.Fromport = from sam.SAMEmit.I2PConfig.Toport = to // Generate the base SESSION CREATE message using emitter baseMsg := strings.TrimSuffix(sam.SAMEmit.Create(), " \n") // Append any extra parameters if provided extraStr := strings.Join(extras, " ") if extraStr != "" { baseMsg += " " + extraStr } // Create final message with proper line termination scmsg := []byte(baseMsg + "\n") log.WithField("message", string(scmsg)).Debug("Sending SESSION CREATE message " + string(scmsg)) conn := sam.Conn n, err := conn.Write(scmsg) if err != nil { log.WithError(err).Error("Failed to write to SAM connection") conn.Close() return nil, oops.Errorf("writing to connection failed: %w", err) } if n != len(scmsg) { log.WithFields(logrus.Fields{ "written": n, "total": len(scmsg), }).Error("Incomplete write to SAM connection") conn.Close() return nil, oops.Errorf("incomplete write to connection: wrote %d bytes, expected %d bytes", n, len(scmsg)) } 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, oops.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, oops.Errorf("SAMv3 created a tunnel with keys other than the ones we asked it for") } log.Debug("Successfully created new session") return &BaseSession{ id: id, conn: conn, keys: keys, SAM: sam, }, nil } else if text == SESSION_DUPLICATE_ID { log.Error("Duplicate tunnel name") conn.Close() return nil, oops.Errorf("Duplicate tunnel name") } else if text == SESSION_DUPLICATE_DEST { log.Error("Duplicate destination") conn.Close() return nil, oops.Errorf("Duplicate destination") } else if text == SESSION_INVALID_KEY { log.Error("Invalid key for SAM session") conn.Close() return nil, oops.Errorf("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, oops.Errorf("I2P error " + text[len(SESSION_I2P_ERROR):]) } else { log.WithField("reply", text).Error("Unable to parse SAMv3 reply") conn.Close() return nil, oops.Errorf("Unable to parse SAMv3 reply: " + text) } }