Browse Source

Add support for importing embedded server entry list

Rod Hynes 11 years ago
parent
commit
ce95263e80
5 changed files with 83 additions and 17 deletions
  1. 40 1
      ConsoleClient/psiphonClient.go
  2. 1 1
      psiphon/conn.go
  3. 16 3
      psiphon/dataStore.go
  4. 10 11
      psiphon/remoteServerList.go
  5. 16 1
      psiphon/serverEntry.go

+ 40 - 1
ConsoleClient/psiphonClient.go

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Psiphon Inc.
+ * Copyright (c) 2015, Psiphon Inc.
  * All rights reserved.
  *
  * This program is free software: you can redistribute it and/or modify
@@ -33,14 +33,21 @@ import (
 
 func main() {
 
+	// Define command-line parameters
+
 	var configFilename string
 	flag.StringVar(&configFilename, "config", "", "configuration input file")
 
+	var embeddedServerEntryListFilename string
+	flag.StringVar(&embeddedServerEntryListFilename, "serverList", "", "embedded server entry list input file")
+
 	var profileFilename string
 	flag.StringVar(&profileFilename, "profile", "", "CPU profile output file")
 
 	flag.Parse()
 
+	// Handle required config file parameter
+
 	if configFilename == "" {
 		log.Fatalf("configuration file is required")
 	}
@@ -53,6 +60,8 @@ func main() {
 		log.Fatalf("error processing configuration file: %s", err)
 	}
 
+	// Handle optional profiling parameter
+
 	if profileFilename != "" {
 		profileFile, err := os.Create(profileFilename)
 		if err != nil {
@@ -62,11 +71,37 @@ func main() {
 		defer pprof.StopCPUProfile()
 	}
 
+	// Initialize data store
+
 	err = psiphon.InitDataStore(config)
 	if err != nil {
 		log.Fatalf("error initializing datastore: %s", err)
 	}
 
+	// Handle optional embedded server list file parameter
+	// If specified, the embedded server list is loaded and stored before
+	// running Psiphon.
+
+	if embeddedServerEntryListFilename != "" {
+		serverEntryList, err := ioutil.ReadFile(embeddedServerEntryListFilename)
+		if err != nil {
+			log.Fatalf("error loading embedded server entry list file: %s", err)
+		}
+		// TODO: stream embedded server list data? also, the cast makaes an unnecessary copy of a large buffer?
+		serverEntries, err := psiphon.DecodeServerEntryList(string(serverEntryList))
+		if err != nil {
+			log.Fatalf("error decoding embedded server entry list file: %s", err)
+		}
+		// Since embedded server list entries may become stale, they will not
+		// overwrite existing stored entries for the same server.
+		err = psiphon.StoreServerEntries(serverEntries, false)
+		if err != nil {
+			log.Fatalf("error storing embedded server entry list data: %s", err)
+		}
+	}
+
+	// Set logfile, if configured
+
 	if config.LogFilename != "" {
 		logFile, err := os.OpenFile(config.LogFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
 		if err != nil {
@@ -76,6 +111,8 @@ func main() {
 		log.SetOutput(logFile)
 	}
 
+	// Run Psiphon
+
 	controller := psiphon.NewController(config)
 	shutdownBroadcast := make(chan struct{})
 	controllerWaitGroup := new(sync.WaitGroup)
@@ -85,6 +122,8 @@ func main() {
 		controller.Run(shutdownBroadcast)
 	}()
 
+	// Wait for an OS signal, then stop Psiphon and exit
+
 	systemStopSignal := make(chan os.Signal, 1)
 	signal.Notify(systemStopSignal, os.Interrupt, os.Kill)
 	<-systemStopSignal

+ 1 - 1
psiphon/conn.go

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Psiphon Inc.
+ * Copyright (c) 2015, Psiphon Inc.
  * All rights reserved.
  *
  * This program is free software: you can redistribute it and/or modify

+ 16 - 3
psiphon/dataStore.go

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Psiphon Inc.
+ * Copyright (c) 2015, Psiphon Inc.
  * All rights reserved.
  *
  * This program is free software: you can redistribute it and/or modify
@@ -171,7 +171,7 @@ func StoreServerEntry(serverEntry *ServerEntry, replaceIfExists bool) error {
 			return ContextError(err)
 		}
 		if serverEntryExists && !replaceIfExists {
-			// Nothing more to do
+			Notice(NOTICE_INFO, "ignored update for server %s", serverEntry.IpAddress)
 			return nil
 		}
 		_, err = transaction.Exec(`
@@ -215,12 +215,25 @@ func StoreServerEntry(serverEntry *ServerEntry, replaceIfExists bool) error {
 		}
 		// TODO: post notice after commit
 		if !serverEntryExists {
-			Notice(NOTICE_INFO, "stored server %s", serverEntry.IpAddress)
+			Notice(NOTICE_INFO, "updated server %s", serverEntry.IpAddress)
 		}
 		return nil
 	})
 }
 
+// StoreServerEntries stores a list of server entries. This is simply a
+// helper which calls StoreServerEntry on each entry in the list -- so there
+// is an independent transaction for each entry -- and stops on first error.
+func StoreServerEntries(serverEntries []*ServerEntry, replaceIfExists bool) error {
+	for _, serverEntry := range serverEntries {
+		err := StoreServerEntry(serverEntry, replaceIfExists)
+		if err != nil {
+			return ContextError(err)
+		}
+	}
+	return nil
+}
+
 // PromoteServerEntry assigns the top rank (one more than current
 // max rank) to the specified server entry. Server candidates are
 // iterated in decending rank order, so this server entry will be

+ 10 - 11
psiphon/remoteServerList.go

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Psiphon Inc.
+ * Copyright (c) 2015, Psiphon Inc.
  * All rights reserved.
  *
  * This program is free software: you can redistribute it and/or modify
@@ -29,7 +29,6 @@ import (
 	"errors"
 	"io/ioutil"
 	"net/http"
-	"strings"
 )
 
 // RemoteServerList is a JSON record containing a list of Psiphon server
@@ -84,16 +83,16 @@ func FetchRemoteServerList(config *Config, pendingConns *Conns) (err error) {
 		return ContextError(err)
 	}
 
-	for _, encodedServerEntry := range strings.Split(remoteServerList.Data, "\n") {
-		serverEntry, err := DecodeServerEntry(encodedServerEntry)
-		if err != nil {
-			return ContextError(err)
-		}
-		err = StoreServerEntry(serverEntry, true)
-		if err != nil {
-			return ContextError(err)
-		}
+	serverEntries, err := DecodeServerEntryList(remoteServerList.Data)
+	if err != nil {
+		return ContextError(err)
 	}
+
+	err = StoreServerEntries(serverEntries, true)
+	if err != nil {
+		return ContextError(err)
+	}
+
 	return nil
 }
 

+ 16 - 1
psiphon/serverEntry.go

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Psiphon Inc.
+ * Copyright (c) 2015, Psiphon Inc.
  * All rights reserved.
  *
  * This program is free software: you can redistribute it and/or modify
@@ -24,6 +24,7 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"errors"
+	"strings"
 )
 
 // ServerEntry represents a Psiphon server. It contains information
@@ -69,3 +70,17 @@ func DecodeServerEntry(encodedServerEntry string) (serverEntry *ServerEntry, err
 	}
 	return serverEntry, nil
 }
+
+// DecodeServerEntryList extracts server entries from the list encoding
+// used by remote server lists and Psiphon server handshake requests.
+func DecodeServerEntryList(encodedServerEntryList string) (serverEntries []*ServerEntry, err error) {
+	serverEntries = make([]*ServerEntry, 0)
+	for _, encodedServerEntry := range strings.Split(encodedServerEntryList, "\n") {
+		serverEntry, err := DecodeServerEntry(encodedServerEntry)
+		if err != nil {
+			return nil, ContextError(err)
+		}
+		serverEntries = append(serverEntries, serverEntry)
+	}
+	return serverEntries, nil
+}