Просмотр исходного кода

Added support for selecting egress region

Rod Hynes 11 лет назад
Родитель
Сommit
1c0c7be680
4 измененных файлов с 40 добавлено и 21 удалено
  1. 0 1
      README.md
  2. 1 0
      psiphon/config.go
  3. 33 16
      psiphon/dataStore.go
  4. 6 4
      psiphon/runTunnel.go

+ 0 - 1
README.md

@@ -16,7 +16,6 @@ This project is currently at the proof-of-concept stage. Current production Psip
 ### TODO (proof-of-concept)
 
 * shutdown results in log noise: "use of closed network connection"
-* region preference
 * use ContextError in more places
 * psiphon.Conn for Windows
 * build/test on Android and iOS

+ 1 - 0
psiphon/config.go

@@ -34,6 +34,7 @@ type Config struct {
 	ClientVersion                      int
 	ClientPlatform                     string
 	TunnelWholeDevice                  int
+	EgressRegion                       string
 }
 
 // LoadConfig reads, and parse, and validates a JSON format Psiphon config

+ 33 - 16
psiphon/dataStore.go

@@ -44,8 +44,9 @@ func initDataStore() {
 		const schema = `
         create table if not exists serverEntry
             (id text not null primary key,
-             data blob not null,
-             rank integer not null unique);
+             rank integer not null unique,
+             region text not null,
+             data blob not null);
         create table if not exists keyValue
             (key text not null,
              value text not null);
@@ -126,9 +127,9 @@ func StoreServerEntry(serverEntry *ServerEntry, replaceIfExists bool) error {
 			return ContextError(err)
 		}
 		_, err = transaction.Exec(`
-            insert or replace into serverEntry (id, data, rank)
-            values (?, ?, (select coalesce(max(rank)-1, 0) from serverEntry));
-            `, serverEntry.IpAddress, data)
+            insert or replace into serverEntry (id, rank, region, data)
+            values (?, (select coalesce(max(rank)-1, 0) from serverEntry), ?, ?);
+            `, serverEntry.IpAddress, serverEntry.Region, data)
 		// TODO: log after commit
 		if !serverEntryExists {
 			log.Printf("stored server %s", serverEntry.IpAddress)
@@ -157,15 +158,16 @@ func PromoteServerEntry(ipAddress string) error {
 // ServerEntryCycler is used to continuously iterate over
 // stored server entries in rank order.
 type ServerEntryCycler struct {
+	region      string
 	transaction *sql.Tx
 	cursor      *sql.Rows
 	isReset     bool
 }
 
 // NewServerEntryCycler creates a new ServerEntryCycler
-func NewServerEntryCycler() (cycler *ServerEntryCycler, err error) {
+func NewServerEntryCycler(region string) (cycler *ServerEntryCycler, err error) {
 	initDataStore()
-	cycler = new(ServerEntryCycler)
+	cycler = &ServerEntryCycler{region: region}
 	err = cycler.Reset()
 	if err != nil {
 		return nil, err
@@ -181,7 +183,15 @@ func (cycler *ServerEntryCycler) Reset() error {
 	if err != nil {
 		return ContextError(err)
 	}
-	cursor, err := transaction.Query("select * from serverEntry order by rank desc;")
+	var cursor *sql.Rows
+	if cycler.region == "" {
+		cursor, err = transaction.Query(
+			"select data from serverEntry order by rank desc;")
+	} else {
+		cursor, err = transaction.Query(
+			"select data from serverEntry where region = ? order by rank desc;",
+			cycler.region)
+	}
 	if err != nil {
 		transaction.Rollback()
 		return ContextError(err)
@@ -227,10 +237,8 @@ func (cycler *ServerEntryCycler) Next() (serverEntry *ServerEntry, err error) {
 		}
 	}
 	cycler.isReset = false
-	var id string
 	var data []byte
-	var rank int64
-	err = cycler.cursor.Scan(&id, &data, &rank)
+	err = cycler.cursor.Scan(&data)
 	if err != nil {
 		return nil, ContextError(err)
 	}
@@ -243,13 +251,22 @@ func (cycler *ServerEntryCycler) Next() (serverEntry *ServerEntry, err error) {
 }
 
 // HasServerEntries returns true if the data store contains at
-// least one server entry.
-func HasServerEntries() bool {
+// least one server entry (for the specified region, in not blank).
+func HasServerEntries(region string) bool {
 	initDataStore()
+	var err error
 	var count int
-	err := singleton.db.QueryRow("select count(*) from serverEntry;").Scan(&count)
-	if err == nil {
-		log.Printf("stored servers: %d", count)
+	if region == "" {
+		err = singleton.db.QueryRow("select count(*) from serverEntry;").Scan(&count)
+		if err == nil {
+			log.Printf("stored servers: %d", count)
+		}
+	} else {
+		err = singleton.db.QueryRow(
+			"select count(*) from serverEntry where region = ?;", region).Scan(&count)
+		if err == nil {
+			log.Printf("stored servers for region %s: %d", region, count)
+		}
 	}
 	return err == nil && count > 0
 }

+ 6 - 4
psiphon/runTunnel.go

@@ -50,7 +50,8 @@ func establishTunnelWorker(
 		if IsSignalled(broadcastStopWorkers) {
 			return
 		}
-		log.Printf("connecting to %s", serverEntry.IpAddress)
+		log.Printf("connecting to %s in region %s",
+			serverEntry.IpAddress, serverEntry.Region)
 		tunnel, err := EstablishTunnel(serverEntry, pendingConns)
 		if err != nil {
 			// TODO: distingush case where conn is interrupted?
@@ -96,9 +97,10 @@ func runTunnel(config *Config) error {
 	// TODO: add a throttle after each full cycle?
 	// Note: errors fall through to ensure worker and channel cleanup
 	var selectedTunnel *Tunnel
-	cycler, err := NewServerEntryCycler()
+	cycler, err := NewServerEntryCycler(config.EgressRegion)
 	for selectedTunnel == nil && err == nil {
-		serverEntry, err := cycler.Next()
+		var serverEntry *ServerEntry
+		serverEntry, err = cycler.Next() // don't mask err here, we want to reference it after the loop
 		if err != nil {
 			break
 		}
@@ -191,7 +193,7 @@ func RunTunnelForever(config *Config) {
 		}
 	}()
 	for {
-		if HasServerEntries() {
+		if HasServerEntries(config.EgressRegion) {
 			err := runTunnel(config)
 			if err != nil {
 				log.Printf("run tunnel error: %s", err)