package gosam import ( "errors" "fmt" "strconv" "strings" ) // Option is a client Option type Option func(*Client) error // SetAddr sets a clients's address in the form host:port or host, port func SetAddr(s ...string) func(*Client) error { return func(c *Client) error { if len(s) == 1 { split := strings.SplitN(s[0], ":", 2) if len(split) == 2 { if i, err := strconv.Atoi(split[1]); err == nil { if i < 65536 { c.host = split[0] c.port = split[1] return nil } return errors.New("invalid port") } return errors.New("invalid port; non-number") } return fmt.Errorf("invalid address; use host:port %s", split) } else if len(s) == 2 { if i, err := strconv.Atoi(s[1]); err == nil { if i < 65536 { c.host = s[0] c.port = s[1] return nil } return errors.New("invalid port") } return errors.New("invalid port; non-number") } else { return errors.New("invalid address") } } } // SetAddrMixed sets a clients's address in the form host, port(int) func SetAddrMixed(s string, i int) func(*Client) error { return func(c *Client) error { if i < 65536 && i > 0 { c.host = s c.port = strconv.Itoa(i) return nil } return errors.New("invalid port") } } // SetHost sets the host of the client's SAM bridge func SetHost(s string) func(*Client) error { return func(c *Client) error { c.host = s return nil } } // SetUser sets the username for authentication during the SAM HELLO phase func SetUser(s string) func(*Client) error { return func(c *Client) error { c.user = s return nil } } // SetUser sets the password for authentication during the SAM HELLO phase func SetPass(s string) func(*Client) error { return func(c *Client) error { c.pass = s return nil } } func SetSAMMinVersion(i int) func(*Client) error { return func(c *Client) error { if i < 0 { return errors.New("SAM version must be greater than or equal to 0") } if i > 3 { return errors.New("SAM version must be less than or equal to 3") } c.sammin = i return nil } } func SetSAMMaxVersion(i int) func(*Client) error { return func(c *Client) error { if i < 0 { return errors.New("SAM version must be greater than or equal to 0") } if i > 3 { return errors.New("SAM version must be less than or equal to 3") } c.sammin = i return nil } } // SetLocalDestination sets the local destination of the tunnel from a private // key func SetLocalDestination(s string) func(*Client) error { return func(c *Client) error { c.destination = s return nil } } func setid(s int32) func(*Client) error { return func(c *Client) error { c.id = s return nil } } // SetPort sets the port of the client's SAM bridge using a string func SetPort(s string) func(*Client) error { return func(c *Client) error { port, err := strconv.Atoi(s) if err != nil { return errors.New("invalid port; non-number") } if port < 65536 && port > -1 { c.port = s return nil } return errors.New("invalid port") } } // SetPortInt sets the port of the client's SAM bridge using a string func SetPortInt(i int) func(*Client) error { return func(c *Client) error { if i < 65536 && i > -1 { c.port = strconv.Itoa(i) return nil } return errors.New("invalid port") } } // SetFromPort sets the port of the client's SAM bridge using a string func SetFromPort(s string) func(*Client) error { return func(c *Client) error { port, err := strconv.Atoi(s) if err != nil { return errors.New("invalid port; non-number") } if port < 65536 && port > -1 { c.fromport = s return nil } return errors.New("invalid port") } } // SetFromPortInt sets the port of the client's SAM bridge using a string func SetFromPortInt(i int) func(*Client) error { return func(c *Client) error { if i < 65536 && i > -1 { c.fromport = strconv.Itoa(i) return nil } return errors.New("invalid port") } } // SetToPort sets the port of the client's SAM bridge using a string func SetToPort(s string) func(*Client) error { return func(c *Client) error { port, err := strconv.Atoi(s) if err != nil { return errors.New("invalid port; non-number") } if port < 65536 && port > -1 { c.toport = s return nil } return errors.New("invalid port") } } // SetToPortInt sets the port of the client's SAM bridge using a string func SetToPortInt(i int) func(*Client) error { return func(c *Client) error { if i < 65536 && i > -1 { c.fromport = strconv.Itoa(i) return nil } return errors.New("invalid port") } } // SetDebug enables debugging messages func SetDebug(b bool) func(*Client) error { return func(c *Client) error { //c.debug = b c.debug = true return nil } } // SetInLength sets the number of hops inbound func SetInLength(u uint) func(*Client) error { return func(c *Client) error { if u < 7 { c.inLength = u return nil } return errors.New("invalid inbound tunnel length") } } // SetOutLength sets the number of hops outbound func SetOutLength(u uint) func(*Client) error { return func(c *Client) error { if u < 7 { c.outLength = u return nil } return errors.New("invalid outbound tunnel length") } } // SetInVariance sets the variance of a number of hops inbound func SetInVariance(i int) func(*Client) error { return func(c *Client) error { if i < 7 && i > -7 { c.inVariance = i return nil } return errors.New("invalid inbound tunnel length") } } // SetOutVariance sets the variance of a number of hops outbound func SetOutVariance(i int) func(*Client) error { return func(c *Client) error { if i < 7 && i > -7 { c.outVariance = i return nil } return errors.New("invalid outbound tunnel variance") } } // SetInQuantity sets the inbound tunnel quantity func SetInQuantity(u uint) func(*Client) error { return func(c *Client) error { if u <= 16 { c.inQuantity = u return nil } return errors.New("invalid inbound tunnel quantity") } } // SetOutQuantity sets the outbound tunnel quantity func SetOutQuantity(u uint) func(*Client) error { return func(c *Client) error { if u <= 16 { c.outQuantity = u return nil } return errors.New("invalid outbound tunnel quantity") } } // SetInBackups sets the inbound tunnel backups func SetInBackups(u uint) func(*Client) error { return func(c *Client) error { if u < 6 { c.inBackups = u return nil } return errors.New("invalid inbound tunnel backup quantity") } } // SetOutBackups sets the inbound tunnel backups func SetOutBackups(u uint) func(*Client) error { return func(c *Client) error { if u < 6 { c.outBackups = u return nil } return errors.New("invalid outbound tunnel backup quantity") } } // SetUnpublished tells the router to not publish the client leaseset func SetUnpublished(b bool) func(*Client) error { return func(c *Client) error { c.dontPublishLease = b return nil } } // SetEncrypt tells the router to use an encrypted leaseset func SetEncrypt(b bool) func(*Client) error { return func(c *Client) error { c.encryptLease = b return nil } } // SetLeaseSetEncType tells the router to use an encrypted leaseset of a specific type. // defaults to 4,0 func SetLeaseSetEncType(b string) func(*Client) error { return func(c *Client) error { c.leaseSetEncType = b return nil } } // SetReduceIdle sets the created tunnels to be reduced during extended idle time to avoid excessive resource usage func SetReduceIdle(b bool) func(*Client) error { return func(c *Client) error { c.reduceIdle = b return nil } } // SetReduceIdleTime sets time to wait before the tunnel quantity is reduced func SetReduceIdleTime(u uint) func(*Client) error { return func(c *Client) error { if u > 299999 { c.reduceIdleTime = u return nil } return fmt.Errorf("invalid reduce idle time %v", u) } } // SetReduceIdleQuantity sets number of tunnels to keep alive during an extended idle period func SetReduceIdleQuantity(u uint) func(*Client) error { return func(c *Client) error { if u < 5 { c.reduceIdleQuantity = u return nil } return fmt.Errorf("invalid reduced tunnel quantity %v", u) } } // SetCloseIdle sets the tunnels to close after a specific amount of time func SetCloseIdle(b bool) func(*Client) error { return func(c *Client) error { c.closeIdle = b return nil } } // SetCloseIdleTime sets the time in milliseconds to wait before closing tunnels func SetCloseIdleTime(u uint) func(*Client) error { return func(c *Client) error { if u > 299999 { c.closeIdleTime = u return nil } return fmt.Errorf("invalid close idle time %v", u) } } // SetCompression sets the tunnels to close after a specific amount of time func SetCompression(b bool) func(*Client) error { return func(c *Client) error { c.compress = b return nil } } /* SAM v 3.1 Options*/ // SetSignatureType tells gosam to pass SAM a signature_type parameter with one // of the following values: // // "SIGNATURE_TYPE=DSA_SHA1", // "SIGNATURE_TYPE=ECDSA_SHA256_P256", // "SIGNATURE_TYPE=ECDSA_SHA384_P384", // "SIGNATURE_TYPE=ECDSA_SHA512_P521", // "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519", // // or an empty string func SetSignatureType(s string) func(*Client) error { return func(c *Client) error { if s == "" { c.sigType = "" return nil } for _, valid := range SAMsigTypes { if s == valid { c.sigType = valid return nil } } return errors.New("invalid signature type specified at construction time") } } // return the from port as a string. func (c *Client) from() string { if c.fromport == "FROM_PORT=0" { return "" } if c.fromport == "0" { return "" } if c.fromport == "" { return "" } return fmt.Sprintf(" FROM_PORT=%v ", c.fromport) } // return the to port as a string. func (c *Client) to() string { if c.fromport == "TO_PORT=0" { return "" } if c.fromport == "0" { return "" } if c.toport == "" { return "" } return fmt.Sprintf(" TO_PORT=%v ", c.toport) } // return the signature type as a string. func (c *Client) sigtype() string { return fmt.Sprintf(" %s ", c.sigType) } // return the inbound length as a string. func (c *Client) inlength() string { return fmt.Sprintf(" inbound.length=%d ", c.inLength) } // return the outbound length as a string. func (c *Client) outlength() string { return fmt.Sprintf(" outbound.length=%d ", c.outLength) } // return the inbound length variance as a string. func (c *Client) invariance() string { return fmt.Sprintf(" inbound.lengthVariance=%d ", c.inVariance) } // return the outbound length variance as a string. func (c *Client) outvariance() string { return fmt.Sprintf(" outbound.lengthVariance=%d ", c.outVariance) } // return the inbound tunnel quantity as a string. func (c *Client) inquantity() string { return fmt.Sprintf(" inbound.quantity=%d ", c.inQuantity) } // return the outbound tunnel quantity as a string. func (c *Client) outquantity() string { return fmt.Sprintf(" outbound.quantity=%d ", c.outQuantity) } // return the inbound tunnel quantity as a string. func (c *Client) inbackups() string { return fmt.Sprintf(" inbound.backupQuantity=%d ", c.inQuantity) } // return the outbound tunnel quantity as a string. func (c *Client) outbackups() string { return fmt.Sprintf(" outbound.backupQuantity=%d ", c.outQuantity) } func (c *Client) encryptlease() string { if c.encryptLease { return " i2cp.encryptLeaseSet=true " } return " i2cp.encryptLeaseSet=false " } func (c *Client) leasesetenctype() string { if c.encryptLease { return fmt.Sprintf(" i2cp.leaseSetEncType=%s ", c.leaseSetEncType) } return " i2cp.leaseSetEncType=4,0 " } func (c *Client) dontpublishlease() string { if c.dontPublishLease { return " i2cp.dontPublishLeaseSet=true " } return " i2cp.dontPublishLeaseSet=false " } func (c *Client) closeonidle() string { if c.closeIdle { return " i2cp.closeOnIdle=true " } return " i2cp.closeOnIdle=false " } func (c *Client) closeidletime() string { return fmt.Sprintf(" i2cp.closeIdleTime=%d ", c.closeIdleTime) } func (c *Client) reduceonidle() string { if c.reduceIdle { return " i2cp.reduceOnIdle=true " } return " i2cp.reduceOnIdle=false " } func (c *Client) reduceidletime() string { return fmt.Sprintf(" i2cp.reduceIdleTime=%d ", c.reduceIdleTime) } func (c *Client) reduceidlecount() string { return fmt.Sprintf(" i2cp.reduceIdleQuantity=%d ", c.reduceIdleQuantity) } func (c *Client) compression() string { if c.compress { return " i2cp.gzip=true " } return " i2cp.gzip=false " } // return all options as string ready for passing to sendcmd func (c *Client) allOptions() string { return c.inlength() + c.outlength() + c.invariance() + c.outvariance() + c.inquantity() + c.outquantity() + c.inbackups() + c.outbackups() + c.dontpublishlease() + c.encryptlease() + c.leasesetenctype() + c.reduceonidle() + c.reduceidletime() + c.reduceidlecount() + c.closeonidle() + c.closeidletime() + c.compression() } // Print return all options as string func (c *Client) Print() string { return c.inlength() + c.outlength() + c.invariance() + c.outvariance() + c.inquantity() + c.outquantity() + c.inbackups() + c.outbackups() + c.dontpublishlease() + c.encryptlease() + c.leasesetenctype() + c.reduceonidle() + c.reduceidletime() + c.reduceidlecount() + c.closeonidle() + c.closeidletime() + c.compression() } func (c *Client) getUser() string { if c.user == "" { return "" } return fmt.Sprintf("USER=%s", c.user) } func (c *Client) getPass() string { if c.pass == "" { return "" } return fmt.Sprintf("PASSWORD=%s", c.pass) }