Преглед изворни кода

Add support for server entry versioning

Rod Hynes пре 8 година
родитељ
комит
a4157f25b1
4 измењених фајлова са 25 додато и 10 уклоњено
  1. 3 2
      psiphon/common/protocol/serverEntry.go
  2. 17 8
      psiphon/dataStore.go
  3. 1 0
      psiphon/server/config.go
  4. 4 0
      psiphon/server/psinet/psinet.go

+ 3 - 2
psiphon/common/protocol/serverEntry.go

@@ -61,6 +61,7 @@ type ServerEntry struct {
 	MeekFrontingDisableSNI        bool     `json:"meekFrontingDisableSNI"`
 	MeekFrontingDisableSNI        bool     `json:"meekFrontingDisableSNI"`
 	TacticsRequestPublicKey       string   `json:"tacticsRequestPublicKey"`
 	TacticsRequestPublicKey       string   `json:"tacticsRequestPublicKey"`
 	TacticsRequestObfuscatedKey   string   `json:"tacticsRequestObfuscatedKey"`
 	TacticsRequestObfuscatedKey   string   `json:"tacticsRequestObfuscatedKey"`
+	ConfigurationVersion          int      `json:"configurationVersion"`
 
 
 	// These local fields are not expected to be present in downloaded server
 	// These local fields are not expected to be present in downloaded server
 	// entries. They are added by the client to record and report stats about
 	// entries. They are added by the client to record and report stats about
@@ -228,8 +229,8 @@ func DecodeServerEntry(
 func ValidateServerEntry(serverEntry *ServerEntry) error {
 func ValidateServerEntry(serverEntry *ServerEntry) error {
 	ipAddr := net.ParseIP(serverEntry.IpAddress)
 	ipAddr := net.ParseIP(serverEntry.IpAddress)
 	if ipAddr == nil {
 	if ipAddr == nil {
-		errMsg := fmt.Sprintf("server entry has invalid ipAddress: '%s'", serverEntry.IpAddress)
-		return common.ContextError(errors.New(errMsg))
+		return common.ContextError(
+			fmt.Errorf("server entry has invalid ipAddress: '%s'", serverEntry.IpAddress))
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 17 - 8
psiphon/dataStore.go

@@ -204,8 +204,11 @@ func checkInitDataStore() {
 // rank for iteration order (the previous top ranked entry is promoted). The
 // rank for iteration order (the previous top ranked entry is promoted). The
 // purpose of inserting at next-to-top is to keep the last selected server
 // purpose of inserting at next-to-top is to keep the last selected server
 // as the top ranked server.
 // as the top ranked server.
-// When replaceIfExists is true, an existing server entry record is
-// overwritten; otherwise, the existing record is unchanged.
+//
+// When a server entry already exists for a given server, it will be
+// replaced only if replaceIfExists is set or if the the ConfigurationVersion
+// field of the new entry is strictly higher than the existing entry.
+//
 // If the server entry data is malformed, an alert notice is issued and
 // If the server entry data is malformed, an alert notice is issued and
 // the entry is skipped; no error is returned.
 // the entry is skipped; no error is returned.
 func StoreServerEntry(serverEntry *protocol.ServerEntry, replaceIfExists bool) error {
 func StoreServerEntry(serverEntry *protocol.ServerEntry, replaceIfExists bool) error {
@@ -215,7 +218,8 @@ func StoreServerEntry(serverEntry *protocol.ServerEntry, replaceIfExists bool) e
 	// so instead of skipping we fail with an error.
 	// so instead of skipping we fail with an error.
 	err := protocol.ValidateServerEntry(serverEntry)
 	err := protocol.ValidateServerEntry(serverEntry)
 	if err != nil {
 	if err != nil {
-		return common.ContextError(errors.New("invalid server entry"))
+		return common.ContextError(
+			fmt.Errorf("invalid server entry: %s", err))
 	}
 	}
 
 
 	// BoltDB implementation note:
 	// BoltDB implementation note:
@@ -232,16 +236,21 @@ func StoreServerEntry(serverEntry *protocol.ServerEntry, replaceIfExists bool) e
 
 
 		// Check not only that the entry exists, but is valid. This
 		// Check not only that the entry exists, but is valid. This
 		// will replace in the rare case where the data is corrupt.
 		// will replace in the rare case where the data is corrupt.
-		existingServerEntryValid := false
+		existingConfigurationVersion := -1
 		existingData := serverEntries.Get([]byte(serverEntry.IpAddress))
 		existingData := serverEntries.Get([]byte(serverEntry.IpAddress))
 		if existingData != nil {
 		if existingData != nil {
-			existingServerEntry := new(protocol.ServerEntry)
-			if json.Unmarshal(existingData, existingServerEntry) == nil {
-				existingServerEntryValid = true
+			var existingServerEntry *protocol.ServerEntry
+			err := json.Unmarshal(existingData, &existingServerEntry)
+			if err == nil {
+				existingConfigurationVersion = existingServerEntry.ConfigurationVersion
 			}
 			}
 		}
 		}
 
 
-		if existingServerEntryValid && !replaceIfExists {
+		exists := existingConfigurationVersion > -1
+		newer := exists && existingConfigurationVersion > serverEntry.ConfigurationVersion
+		update := !exists || replaceIfExists || newer
+
+		if !update {
 			// Disabling this notice, for now, as it generates too much noise
 			// Disabling this notice, for now, as it generates too much noise
 			// in diagnostics with clients that always submit embedded servers
 			// in diagnostics with clients that always submit embedded servers
 			// to the core on each run.
 			// to the core on each run.

+ 1 - 0
psiphon/server/config.go

@@ -717,6 +717,7 @@ func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, []byte, error
 		MeekFrontingDisableSNI:        false,
 		MeekFrontingDisableSNI:        false,
 		TacticsRequestPublicKey:       params.TacticsRequestPublicKey,
 		TacticsRequestPublicKey:       params.TacticsRequestPublicKey,
 		TacticsRequestObfuscatedKey:   params.TacticsRequestObfuscatedKey,
 		TacticsRequestObfuscatedKey:   params.TacticsRequestObfuscatedKey,
+		ConfigurationVersion:          1,
 	}
 	}
 
 
 	encodedServerEntry, err := protocol.EncodeServerEntry(serverEntry)
 	encodedServerEntry, err := protocol.EncodeServerEntry(serverEntry)

+ 4 - 0
psiphon/server/psinet/psinet.go

@@ -83,6 +83,7 @@ type Server struct {
 	WebServerCertificate        string          `json:"web_server_certificate"`
 	WebServerCertificate        string          `json:"web_server_certificate"`
 	WebServerPort               string          `json:"web_server_port"`
 	WebServerPort               string          `json:"web_server_port"`
 	WebServerSecret             string          `json:"web_server_secret"`
 	WebServerSecret             string          `json:"web_server_secret"`
+	ConfigurationVersion        int             `json:"configuration_version"`
 }
 }
 
 
 type Sponsor struct {
 type Sponsor struct {
@@ -457,6 +458,7 @@ func (db *Database) getEncodedServerEntry(server Server) string {
 		MeekObfuscatedKey             string   `json:"meekObfuscatedKey"`
 		MeekObfuscatedKey             string   `json:"meekObfuscatedKey"`
 		TacticsRequestPublicKey       string   `json:"tacticsRequestPublicKey"`
 		TacticsRequestPublicKey       string   `json:"tacticsRequestPublicKey"`
 		TacticsRequestObfuscatedKey   string   `json:"tacticsRequestObfuscatedKey"`
 		TacticsRequestObfuscatedKey   string   `json:"tacticsRequestObfuscatedKey"`
+		ConfigurationVersion          int      `json:"configurationVersion"`
 	}
 	}
 
 
 	// NOTE: also putting original values in extended config for easier parsing by new clients
 	// NOTE: also putting original values in extended config for easier parsing by new clients
@@ -516,6 +518,8 @@ func (db *Database) getEncodedServerEntry(server Server) string {
 		}
 		}
 	}
 	}
 
 
+	extendedConfig.ConfigurationVersion = server.ConfigurationVersion
+
 	jsonDump, err := json.Marshal(extendedConfig)
 	jsonDump, err := json.Marshal(extendedConfig)
 	if err != nil {
 	if err != nil {
 		return ""
 		return ""