/* * Copyright (c) 2014, Yawning Angel * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // Package transports provides a interface to query supported pluggable // transports. package transports import ( "errors" "fmt" "github.com/OperatorFoundation/shapeshifter-transports/transports/Dust" "github.com/OperatorFoundation/shapeshifter-transports/transports/Optimizer" replicant "github.com/OperatorFoundation/shapeshifter-transports/transports/Replicant" "github.com/OperatorFoundation/shapeshifter-transports/transports/meeklite" "github.com/OperatorFoundation/shapeshifter-transports/transports/obfs4" "github.com/OperatorFoundation/shapeshifter-transports/transports/shadow" "github.com/mufti1/interconv/package" gourl "net/url" "strconv" ) // Transports returns the list of registered transport protocols. func Transports() []string { return []string{"obfs2", "shadow", "obfs4", "Optimizer"} } func ParseArgsObfs4(args map[string]interface{}, target string) (*obfs4.Transport, error) { var cert string var iatMode int untypedCert, ok := args["cert"] if !ok { return nil, errors.New("obfs4 transport missing cert argument") } switch untypedCert.(type) { case string: var icerr error cert, icerr = interconv.ParseString(untypedCert) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for obfs4 cert option") } untypedIatMode, ok2 := args["iatMode"] if !ok2 { return nil, errors.New("obfs4 transport missing iatMode argument") } switch untypedCert.(type) { case string: iatModeStr, icerr := interconv.ParseString(untypedIatMode) if icerr != nil { return nil, icerr } iatModeInt, scerr := strconv.Atoi(iatModeStr) if scerr != nil { return nil, errors.New("obfs4 transport bad iatMode value") } switch iatModeInt { case 0: iatMode = iatModeInt case 1: iatMode = iatModeInt default: return nil, errors.New("unsupported value for obfs4 iatMode option") } case float64: iatModeFloat, icerr := interconv.ParseFloat64(untypedIatMode) if icerr != nil { return nil, icerr } iatModeInt := int(iatModeFloat) switch iatModeInt { case 0: iatMode = iatModeInt case 1: iatMode = iatModeInt default: return nil, errors.New("unsupported value for obfs4 iatMode option") } case int: iatModeInt, icerr := interconv.ParseInt(untypedIatMode) if icerr != nil { return nil, icerr } switch iatModeInt { case 0: iatMode = iatModeInt case 1: iatMode = iatModeInt default: return nil, errors.New("unsupported value for obfs4 iatMode option") } case bool: iatModeBool, icerr := interconv.ParseBoolean(untypedCert) if icerr != nil { return nil, icerr } switch iatModeBool { case true: iatMode = 1 case false: iatMode = 0 } default: return nil, errors.New("unsupported type for obfs4 iatMode option") } transport := obfs4.Transport{ CertString: cert, IatMode: iatMode, Address: target, } return &transport, nil } func ParseArgsShadow(args map[string]interface{}, target string) (*shadow.Transport, error) { var password string var cipherName string untypedPassword, ok := args["password"] if !ok { return nil, errors.New("shadow transport missing password argument") } switch untypedPassword.(type) { case string: var icerr error password, icerr = interconv.ParseString(untypedPassword) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for shadow password option") } untypedCipherName, ok2 := args["cipherName"] if !ok2 { return nil, errors.New("shadow transport missing cipherName argument") } switch untypedCipherName.(type) { case string: var icerr error cipherName, icerr = interconv.ParseString(untypedCipherName) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for shadow cipherName option") } transport := shadow.Transport{ Password: password, CipherName: cipherName, Address: target, } return &transport, nil } func ParseArgsDust(args map[string]interface{}, target string) (*Dust.Transport, error) { var serverPublic string untypedServerPublic, ok := args["serverPublic"] if !ok { return nil, errors.New("dust transport missing serverpublic argument") } switch untypedServerPublic.(type) { case string: var icerr error serverPublic, icerr = interconv.ParseString(untypedServerPublic) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for dust serverpublic option") } transport := Dust.Transport{ ServerPublic: serverPublic, Address: target, } return &transport, nil } func ParseArgsReplicant(args map[string]interface{}, target string) (*replicant.Transport, error) { var conf string fmt.Println(conf) untypedConfig, ok := args["config"] if !ok { return nil, errors.New("replicant transport missing config argument") } switch untypedConfig.(type) { case string: var icerr error conf, icerr = interconv.ParseString(untypedConfig) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for replicant config option") } transport := replicant.Transport{ Config: replicant.Config{}, Address: target, } return &transport, nil } func ParseArgsMeeklite(args map[string]interface{}, target string) (*meeklite.Transport, error) { var url *gourl.URL var front string untypedUrl, ok := args["url"] if !ok { return nil, errors.New("meeklite transport missing url argument") } switch untypedUrl.(type) { case string: urlString, icerr := interconv.ParseString(untypedUrl) if icerr != nil { return nil, icerr } var parseErr error url, parseErr = gourl.Parse(urlString) if parseErr != nil { return nil, errors.New("could not parse URL") } default: return nil, errors.New("unsupported type for meeklite url option") } untypedFront, ok2 := args["front"] if !ok2 { return nil, errors.New("meeklite transport missing front argument") } switch untypedFront.(type) { case string: var icerr error front, icerr = interconv.ParseString(untypedFront) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for meeklite front option") } transport := meeklite.Transport{ Url: url, Front: front, Address: target, } return &transport, nil } func ParseArgsOptimizer(args map[string]interface{}) (*Optimizer.OptimizerTransport, error) { var transports []Optimizer.Transport var strategy *Optimizer.Strategy untypedTransports, ok := args["transports"] if !ok { return nil, errors.New("optimizer transport missing transports argument") } switch untypedTransports.(type) { case []map[string]interface{}: otcs := untypedTransports.([]map[string]interface{}) var parseErr error transports, parseErr = parseTransports(otcs) if parseErr != nil { return nil, errors.New("could not parse transports") } default: return nil, errors.New("unsupported type for Optimizer transports option") } untypedStrategy, ok2 := args["strategy"] if !ok2 { return nil, errors.New("optimizer transport missing strategy argument") } switch untypedStrategy.(type) { case string: strategyString, icerr := interconv.ParseString(untypedStrategy) if icerr != nil { return nil, icerr } var parseErr error strategy, parseErr = parseStrategy(strategyString, transports) if parseErr != nil { return nil, errors.New("could not parse strategy") } default: return nil, errors.New("unsupported type for optimizer strategy option") } transport := Optimizer.OptimizerTransport{ Transports: transports, Strategy: strategy, } return &transport, nil } func parseStrategy(strategyString string, transports []Optimizer.Transport) (*Optimizer.Strategy, error) { switch strategyString { case "first": strategy := Optimizer.NewFirstStrategy(transports) return &strategy, nil case "random": strategy := Optimizer.NewRandomStrategy(transports) return &strategy, nil case "rotate": strategy := Optimizer.NewRotateStrategy(transports) return &strategy, nil case "track": return Optimizer.NewTrackStrategy(transports), nil case "minimizeDialDuration": return Optimizer.NewMinimizeDialDuration(transports), nil default: return nil, errors.New("invalid strategy") } } func parseTransports(otcs []map[string]interface{}) ([]Optimizer.Transport, error) { transports := make([]Optimizer.Transport, len(otcs)) for index, otc := range otcs { transport, err := parsedTransport(otc) if err != nil { return nil, errors.New("transport could not parse config") //this error sucks and is uninformative } transports[index] = transport } return transports, nil } func parsedTransport(otc map[string]interface{}) (Optimizer.Transport, error) { var address string var name string var config map[string]interface{} //start by parsing the address untypedAddress, ok := otc["address"] if !ok { return nil, errors.New("missing address in transport parser") } switch untypedAddress.(type) { case string: var icerr error address, icerr = interconv.ParseString(untypedAddress) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for optimizer address option") } //now to parse the name untypedName, ok2 := otc["name"] if !ok2 { return nil, errors.New("missing name in transport parser") } switch untypedName.(type) { case string: var icerr error name, icerr = interconv.ParseString(untypedName) if icerr != nil { return nil, icerr } default: return nil, errors.New("unsupported type for optimizer name option") } //on to parsing the config untypedConfig, ok3 := otc["config"] if !ok3 { return nil, errors.New("missing config in transport parser") } switch untypedConfig.(type) { case map[string]interface{}: config = untypedConfig.(map[string]interface{}) default: return nil, errors.New("unsupported type for optimizer config option") } switch name { case "shadow": shadowTransport, parseErr := ParseArgsShadow(config, address) if parseErr != nil { return nil, errors.New("could not parse shadow Args") } return shadowTransport, nil case "obfs4": obfs4Transport, parseErr := ParseArgsObfs4(config, address) if parseErr != nil { return nil, errors.New("could not parse obfs4 Args") } return obfs4Transport, nil case "meeklite": meekliteTransport, parseErr := ParseArgsMeeklite(config, address) if parseErr != nil { return nil, errors.New("could not parse meeklite Args") } return meekliteTransport, nil case "Dust": DustTransport, parseErr := ParseArgsDust(config, address) if parseErr != nil { return nil, errors.New("could not parse dust Args") } return DustTransport, nil case "replicant": replicantTransport, parseErr := ParseArgsReplicant(config, address) if parseErr != nil { return nil, errors.New("could not parse replicant Args") } return replicantTransport, nil default: return nil, errors.New("unsupported transport name") } } func ParseReplicantConfig(config string) (replicant.Config, error) { return replicant.Config{}, errors.New("function not implemented") }