commit 58858475e5ba737ca0e7e271b7604fe1ac06bf75 Author: Quinn Jarrell qjarrell@gosynapsify.com Date: Mon Jun 23 16:15:18 2014 -0400
Added a loading configuration file on the server side. The format is identical to the client configuration file except ClientTransportationPlugin is ServerTransportationPlugin. Uses go-shellwords to parse the config files. --- fog-server/Makefile | 2 +- fog-server/config.go | 145 ++++++++++++++++++++++++++++++++++++++++++++++ fog-server/fog-server.go | 80 ++++--------------------- fog-server/fogrc | 13 +++++ fog-server/torrc | 2 +- 5 files changed, 171 insertions(+), 71 deletions(-)
diff --git a/fog-server/Makefile b/fog-server/Makefile index c7d474b..1a153a6 100644 --- a/fog-server/Makefile +++ b/fog-server/Makefile @@ -1,6 +1,6 @@ GOBUILDFLAGS =
-./fog-server: ./fog-server.go ./stack.go +./fog-server: ./fog-server.go ./stack.go ./config.go go build $(GOBUILDFLAGS) -o "$@" $^
test: diff --git a/fog-server/config.go b/fog-server/config.go new file mode 100644 index 0000000..615ea47 --- /dev/null +++ b/fog-server/config.go @@ -0,0 +1,145 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "strings" +) + +import "git.torproject.org/pluggable-transports/goptlib.git" +import "github.com/mattn/go-shellwords" + +// Represents a server transport plugin configuration like: +// ServerTransportPlugin MethodName exec Command +type ServerTransportPlugin struct { + MethodName string + Command []string + Options pt.Args +} + +type Configuration struct { + // Map from method names to command strings. + Transports map[string][]string + // Map from method names to ServerTransportOptions. + Options map[string]pt.Args + // Map from tor-friendly names like "obfs3_websocket" to systematic + // names like "obfs3|websocket". + Aliases map[string]string +} + +func (conf *Configuration) MethodNames() []string { + result := make([]string, 0) + // We understand all the single transports + for k, _ := range conf.Transports { + result = append(result, k) + } + // and aliases. + for k, _ := range conf.Aliases { + result = append(result, k) + } + return result +} + +// Parse a (possibly composed) method name into a slice of single method names. +func (conf *Configuration) ParseMethodName(methodName string) []string { + if name, ok := conf.Aliases[methodName]; ok { + methodName = name + } + return strings.Split(methodName, "|") +} + +func (conf *Configuration) PluginList(methodName string) ([]ServerTransportPlugin, error) { + names := conf.ParseMethodName(methodName) + stp := make([]ServerTransportPlugin, 0) + for _, name := range names { + command, ok := conf.Transports[name] + if !ok { + return nil, errors.New(fmt.Sprintf("no transport named %q", name)) + } + options := conf.Options[name] + stp = append(stp, ServerTransportPlugin{name, command, options}) + } + return stp, nil +} + +// Initialize a configuration object +func getConfiguration() (conf *Configuration) { + conf = new(Configuration) + conf.Transports = make(map[string][]string) + conf.Aliases = make(map[string]string) + conf.Options = make(map[string]pt.Args) + return conf +} + +// Reads a configuration file and returns the contents +func ReadConfigFile(fileName string) (*Configuration, error) { + var contents []byte + contents, err := ioutil.ReadFile(fileName) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error reading configuration file %s contents.", fileName)) + } + return ParseConfiguration(string(contents), getConfiguration()) +} + +// Parses a configuration string and fills the config object's fields with the requested Aliases and ServerTransportPlugins +func ParseConfiguration(configString string, config *Configuration) (*Configuration, error) { + lines := strings.Split(configString, "\n") + for lineCounter, line := range lines { + if len(line) > 0 && line[0] != '#' { // Check for empty lines and comment tags on the first + line = strings.TrimSpace(line) + delimitedTokens, err := shellwords.Parse(line) + if err != nil { + return nil, errors.New(fmt.Sprintf("Line %v: "%v" was split incorrectly by shellwords. Error: %v", lineCounter, line, err)) + } + if len(delimitedTokens) > 1 { + configLineType := delimitedTokens[0] // This can be either Alias or ServerTransportPlugin + if configLineType == "ServerTransportPlugin" { + err = parseTransportLine(config, delimitedTokens, lineCounter) + if err != nil { + return nil, err + } + } else if configLineType == "Alias" { + err = parseAliasLine(config, delimitedTokens, lineCounter) + if err != nil { + return nil, err + } + } else { + log("Configuration file has unknown line %s: %s", lineCounter, line) + } + } + } + } + return config, nil +} + +// Parses a ServerTransportPlugin line. +// Ex: ServerTransportPlugin dummy obfsproxy --client T managed +func parseTransportLine(config *Configuration, tokens []string, lineCounter int) error { + transportName := tokens[1] + transportCmdLine := tokens[2:] + if _, ok := config.Transports[transportName]; ok { + return errors.New(fmt.Sprintf("Configuration file has duplicate ServerTransportPlugin lines. Duplicate line is at line number %s", lineCounter)) + } + config.Transports[transportName] = transportCmdLine + return nil +} + +// Parses an alias line +// Ex: Alias b64_b64 b64|b64 +func parseAliasLine(config *Configuration, tokens []string, lineCounter int) error { + var aliasName string + var aliasPath []string + aliasName = tokens[1] + aliasPath = strings.Split(tokens[2], "|") + if _, hashed := config.Aliases[aliasName]; hashed { + return errors.New(fmt.Sprintf("Configuration file has duplicate Alias lines. Duplicate line is at line number %s", lineCounter)) + } + for _, ptName := range aliasPath { + if _, hashed := config.Transports[ptName]; !hashed { + log("Transport map is missing pluggable transport %s needed for chain %s. Check your configuration file for a ServerTransportPlugin line can launch %s", ptName, aliasName, ptName) + } + } + config.Aliases[aliasName] = tokens[2] + return nil +} \ No newline at end of file diff --git a/fog-server/fog-server.go b/fog-server/fog-server.go index cf77a6a..9ee2703 100644 --- a/fog-server/fog-server.go +++ b/fog-server/fog-server.go @@ -39,6 +39,7 @@ func usage() { fmt.Printf(" -h, --help show this help.\n") fmt.Printf(" --log FILE log messages to FILE (default stderr).\n") fmt.Printf(" --port PORT listen on PORT (overrides Tor's requested port).\n") + fmt.Printf(" -f, --file FILE Loads and runs configuration FILE.\n") }
var logMutex sync.Mutex @@ -174,14 +175,6 @@ func encodeServerTransportOptions(methodName string, opts pt.Args) string { return strings.Join(parts, ";") }
-// Represents a server transport plugin configuration like: -// ServerTransportPlugin MethodName exec Command -type ServerTransportPlugin struct { - MethodName string - Command []string - Options pt.Args -} - func startProcesses(connectBackAddr net.Addr, plugins []ServerTransportPlugin) (bindAddr *net.TCPAddr, procs ProcList, err error) { var stdout io.ReadCloser
@@ -393,72 +386,17 @@ func startChain(methodName string, bindaddr *net.TCPAddr, plugins []ServerTransp return chain, nil }
-type Configuration struct { - // Map from method names to command strings. - Transports map[string][]string - // Map from method names to ServerTransportOptions. - Options map[string]pt.Args - // Map from tor-friendly names like "obfs3_websocket" to systematic - // names like "obfs3|websocket". - Aliases map[string]string -} - -func (conf *Configuration) MethodNames() []string { - result := make([]string, 0) - // We understand all the single transports - for k, _ := range conf.Transports { - result = append(result, k) - } - // and aliases. - for k, _ := range conf.Aliases { - result = append(result, k) - } - return result -} - -// Parse a (possibly composed) method name into a slice of single method names. -func (conf *Configuration) ParseMethodName(methodName string) []string { - if name, ok := conf.Aliases[methodName]; ok { - methodName = name - } - return strings.Split(methodName, "|") -} - -func (conf *Configuration) PluginList(methodName string) ([]ServerTransportPlugin, error) { - names := conf.ParseMethodName(methodName) - stp := make([]ServerTransportPlugin, 0) - for _, name := range names { - command, ok := conf.Transports[name] - if !ok { - return nil, errors.New(fmt.Sprintf("no transport named %q", name)) - } - options := conf.Options[name] - stp = append(stp, ServerTransportPlugin{name, command, options}) - } - return stp, nil -} - -// Simulate loading a configuration file. -func getConfiguration() (conf *Configuration) { - conf = new(Configuration) - conf.Transports = make(map[string][]string) - conf.Aliases = make(map[string]string) - conf.Options = make(map[string]pt.Args) - conf.Transports["obfs3"] = []string{"obfsproxy", "managed"} - conf.Transports["websocket"] = []string{"pt-websocket-server"} - // conf.Options["obfs3"] = make(pt.Args) - // conf.Options["obfs3"]["secret"] = []string{"foo"} - conf.Aliases["obfs3_websocket"] = "obfs3|websocket" - return conf -} - func main() { var logFilename string var port int + var configFilename string + var conf *Configuration
flag.Usage = usage flag.StringVar(&logFilename, "log", "", "log file to write to") flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor") + flag.StringVar(&configFilename, "file", "fogrc", "The fog file to read the configuration from.") + flag.StringVar(&configFilename, "f", "fogrc", "The fog file to read the configuration from.") flag.Parse()
if logFilename != "" { @@ -469,11 +407,15 @@ func main() { } logFile = f } + var err error
+ conf, err = ReadConfigFile(configFilename) + if err != nil { + log("Error in reading configuration file: %s", err) + os.Exit(1) + } log("Starting.")
- var err error - conf := getConfiguration() ptInfo, err = pt.ServerSetup(conf.MethodNames()) if err != nil { log("Error in ServerSetup: %s", err) diff --git a/fog-server/fogrc b/fog-server/fogrc new file mode 100644 index 0000000..870d8b6 --- /dev/null +++ b/fog-server/fogrc @@ -0,0 +1,13 @@ +#Based off of ticket #9744 +#Server transports are setup like so: +#ServerTransportPlugin name commandline +#For instance to launch obfs3, the server transport line should be this +#ServerTransportPlugin obfs3 obfsproxy managed +# +#For chaining transports together, an alias line is used. +#Alias chainname firsttransportname|secondtransportname +#tor expects alias to use underscores instead of pipes. So an alias links the tor version of a plugin chain to the actual plugins. See ticket #9580 + +ServerTransportPlugin obfs3 obfsproxy managed +ServerTransportPlugin websocket pt-websocket-server +Alias obfs3_websocket obfs3|websocket \ No newline at end of file diff --git a/fog-server/torrc b/fog-server/torrc index f103ddc..88258fa 100644 --- a/fog-server/torrc +++ b/fog-server/torrc @@ -2,4 +2,4 @@ ORPort 9999 ExtORPort 5555 BridgeRelay 1 SocksPort 0 -ServerTransportPlugin obfs3_websocket exec ./bin/fog-server +ServerTransportPlugin obfs3_websocket exec ./fog-server
tor-commits@lists.torproject.org