github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/dataStore.go (about)

     1  /*
     2   * Copyright (c) 2015, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package psiphon
    21  
    22  import (
    23  	"bytes"
    24  	"context"
    25  	"encoding/json"
    26  	"io"
    27  	"math"
    28  	"os"
    29  	"strings"
    30  	"sync"
    31  	"time"
    32  
    33  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
    34  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    35  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
    36  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
    37  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    38  )
    39  
    40  var (
    41  	datastoreServerEntriesBucket                = []byte("serverEntries")
    42  	datastoreServerEntryTagsBucket              = []byte("serverEntryTags")
    43  	datastoreServerEntryTombstoneTagsBucket     = []byte("serverEntryTombstoneTags")
    44  	datastoreUrlETagsBucket                     = []byte("urlETags")
    45  	datastoreKeyValueBucket                     = []byte("keyValues")
    46  	datastoreRemoteServerListStatsBucket        = []byte("remoteServerListStats")
    47  	datastoreFailedTunnelStatsBucket            = []byte("failedTunnelStats")
    48  	datastoreSLOKsBucket                        = []byte("SLOKs")
    49  	datastoreTacticsBucket                      = []byte("tactics")
    50  	datastoreSpeedTestSamplesBucket             = []byte("speedTestSamples")
    51  	datastoreDialParametersBucket               = []byte("dialParameters")
    52  	datastoreLastConnectedKey                   = "lastConnected"
    53  	datastoreLastServerEntryFilterKey           = []byte("lastServerEntryFilter")
    54  	datastoreAffinityServerEntryIDKey           = []byte("affinityServerEntryID")
    55  	datastorePersistentStatTypeRemoteServerList = string(datastoreRemoteServerListStatsBucket)
    56  	datastorePersistentStatTypeFailedTunnel     = string(datastoreFailedTunnelStatsBucket)
    57  	datastoreServerEntryFetchGCThreshold        = 10
    58  
    59  	datastoreReferenceCountMutex sync.RWMutex
    60  	datastoreReferenceCount      int64
    61  	datastoreMutex               sync.RWMutex
    62  	activeDatastoreDB            *datastoreDB
    63  )
    64  
    65  // OpenDataStore opens and initializes the singleton datastore instance.
    66  //
    67  // Nested Open/CloseDataStore calls are supported: OpenDataStore will succeed
    68  // when called when the datastore is initialized. Every call to OpenDataStore
    69  // must be paired with a corresponding call to CloseDataStore to ensure the
    70  // datastore is closed.
    71  func OpenDataStore(config *Config) error {
    72  	return openDataStore(config, true)
    73  }
    74  
    75  // OpenDataStoreWithoutRetry performs an OpenDataStore but does not retry or
    76  // reset the datastore file in case of failures. Use
    77  // OpenDataStoreWithoutRetry when the datastore is expected to be locked by
    78  // another process and faster failure is preferred.
    79  func OpenDataStoreWithoutRetry(config *Config) error {
    80  	return openDataStore(config, false)
    81  }
    82  
    83  func openDataStore(config *Config, retryAndReset bool) error {
    84  
    85  	// The datastoreReferenceCountMutex/datastoreMutex mutex pair allow for:
    86  	//
    87  	// _Nested_ OpenDataStore/CloseDataStore calls to not block when a
    88  	// datastoreView is in progress (for example, a GetDialParameters call while
    89  	// a slow ScanServerEntries is running). In this case the nested
    90  	// OpenDataStore/CloseDataStore calls will lock only
    91  	// datastoreReferenceCountMutex and not datastoreMutex.
    92  	//
    93  	// Synchronized access, for OpenDataStore/CloseDataStore, to
    94  	// activeDatastoreDB based on a consistent view of datastoreReferenceCount
    95  	// via locking first datastoreReferenceCount and then datastoreMutex while
    96  	// holding datastoreReferenceCount.
    97  	//
    98  	// Concurrent access, for datastoreView/datastoreUpdate, to activeDatastoreDB
    99  	// via datastoreMutex read locks.
   100  	//
   101  	// Exclusive access, for OpenDataStore/CloseDataStore, to activeDatastoreDB,
   102  	// with no running datastoreView/datastoreUpdate, by aquiring a
   103  	// datastoreMutex write lock.
   104  
   105  	datastoreReferenceCountMutex.Lock()
   106  
   107  	if datastoreReferenceCount < 0 || datastoreReferenceCount == math.MaxInt64 {
   108  		datastoreReferenceCountMutex.Unlock()
   109  		return errors.Tracef(
   110  			"invalid datastore reference count: %d", datastoreReferenceCount)
   111  	}
   112  
   113  	if datastoreReferenceCount > 0 {
   114  
   115  		// For this sanity check, we need only the read-only lock; and must use the
   116  		// read-only lock to allow concurrent datastoreView calls.
   117  
   118  		datastoreMutex.RLock()
   119  		isNil := activeDatastoreDB == nil
   120  		datastoreMutex.RUnlock()
   121  		if isNil {
   122  			return errors.TraceNew("datastore unexpectedly closed")
   123  		}
   124  
   125  		// Add a reference to the open datastore.
   126  
   127  		datastoreReferenceCount += 1
   128  		datastoreReferenceCountMutex.Unlock()
   129  		return nil
   130  	}
   131  
   132  	// Only lock datastoreMutex now that it's necessary.
   133  	// datastoreReferenceCountMutex remains locked.
   134  	datastoreMutex.Lock()
   135  
   136  	if activeDatastoreDB != nil {
   137  		datastoreMutex.Unlock()
   138  		datastoreReferenceCountMutex.Unlock()
   139  		return errors.TraceNew("datastore unexpectedly open")
   140  	}
   141  
   142  	// datastoreReferenceCount is 0, so open the datastore.
   143  
   144  	newDB, err := datastoreOpenDB(
   145  		config.GetDataStoreDirectory(), retryAndReset)
   146  	if err != nil {
   147  		datastoreMutex.Unlock()
   148  		datastoreReferenceCountMutex.Unlock()
   149  		return errors.Trace(err)
   150  	}
   151  
   152  	datastoreReferenceCount = 1
   153  	activeDatastoreDB = newDB
   154  	datastoreMutex.Unlock()
   155  	datastoreReferenceCountMutex.Unlock()
   156  
   157  	_ = resetAllPersistentStatsToUnreported()
   158  
   159  	return nil
   160  }
   161  
   162  // CloseDataStore closes the singleton datastore instance, if open.
   163  func CloseDataStore() {
   164  
   165  	datastoreReferenceCountMutex.Lock()
   166  	defer datastoreReferenceCountMutex.Unlock()
   167  
   168  	if datastoreReferenceCount <= 0 {
   169  		NoticeWarning(
   170  			"invalid datastore reference count: %d", datastoreReferenceCount)
   171  		return
   172  	}
   173  	datastoreReferenceCount -= 1
   174  	if datastoreReferenceCount > 0 {
   175  		return
   176  	}
   177  
   178  	// Only lock datastoreMutex now that it's necessary.
   179  	// datastoreReferenceCountMutex remains locked.
   180  	datastoreMutex.Lock()
   181  	defer datastoreMutex.Unlock()
   182  
   183  	if activeDatastoreDB == nil {
   184  		return
   185  	}
   186  
   187  	err := activeDatastoreDB.close()
   188  	if err != nil {
   189  		NoticeWarning("failed to close datastore: %s", errors.Trace(err))
   190  	}
   191  
   192  	activeDatastoreDB = nil
   193  }
   194  
   195  // GetDataStoreMetrics returns a string logging datastore metrics.
   196  func GetDataStoreMetrics() string {
   197  	datastoreMutex.RLock()
   198  	defer datastoreMutex.RUnlock()
   199  
   200  	if activeDatastoreDB == nil {
   201  		return ""
   202  	}
   203  
   204  	return activeDatastoreDB.getDataStoreMetrics()
   205  }
   206  
   207  // datastoreView runs a read-only transaction, making datastore buckets and
   208  // values available to the supplied function.
   209  //
   210  // Bucket value slices are only valid for the duration of the transaction and
   211  // _must_ not be referenced directly outside the transaction.
   212  func datastoreView(fn func(tx *datastoreTx) error) error {
   213  
   214  	datastoreMutex.RLock()
   215  	defer datastoreMutex.RUnlock()
   216  
   217  	if activeDatastoreDB == nil {
   218  		return errors.TraceNew("datastore not open")
   219  	}
   220  
   221  	err := activeDatastoreDB.view(fn)
   222  	if err != nil {
   223  		err = errors.Trace(err)
   224  	}
   225  	return err
   226  }
   227  
   228  // datastoreUpdate runs a read-write transaction, making datastore buckets and
   229  // values available to the supplied function.
   230  //
   231  // Bucket value slices are only valid for the duration of the transaction and
   232  // _must_ not be referenced directly outside the transaction.
   233  func datastoreUpdate(fn func(tx *datastoreTx) error) error {
   234  
   235  	datastoreMutex.RLock()
   236  	defer datastoreMutex.RUnlock()
   237  
   238  	if activeDatastoreDB == nil {
   239  		return errors.TraceNew("database not open")
   240  	}
   241  
   242  	err := activeDatastoreDB.update(fn)
   243  	if err != nil {
   244  		err = errors.Trace(err)
   245  	}
   246  	return err
   247  }
   248  
   249  // StoreServerEntry adds the server entry to the datastore.
   250  //
   251  // When a server entry already exists for a given server, it will be
   252  // replaced only if replaceIfExists is set or if the the ConfigurationVersion
   253  // field of the new entry is strictly higher than the existing entry.
   254  //
   255  // If the server entry data is malformed, an alert notice is issued and
   256  // the entry is skipped; no error is returned.
   257  func StoreServerEntry(serverEntryFields protocol.ServerEntryFields, replaceIfExists bool) error {
   258  
   259  	// TODO: call serverEntryFields.VerifySignature. At this time, we do not do
   260  	// this as not all server entries have an individual signature field. All
   261  	// StoreServerEntry callers either call VerifySignature or obtain server
   262  	// entries from a trusted source (embedded in a signed client, or in a signed
   263  	// authenticated package).
   264  
   265  	// Server entries should already be validated before this point,
   266  	// so instead of skipping we fail with an error.
   267  	err := protocol.ValidateServerEntryFields(serverEntryFields)
   268  	if err != nil {
   269  		return errors.Tracef("invalid server entry: %s", err)
   270  	}
   271  
   272  	// BoltDB implementation note:
   273  	// For simplicity, we don't maintain indexes on server entry
   274  	// region or supported protocols. Instead, we perform full-bucket
   275  	// scans with a filter. With a small enough database (thousands or
   276  	// even tens of thousand of server entries) and common enough
   277  	// values (e.g., many servers support all protocols), performance
   278  	// is expected to be acceptable.
   279  
   280  	err = datastoreUpdate(func(tx *datastoreTx) error {
   281  
   282  		serverEntries := tx.bucket(datastoreServerEntriesBucket)
   283  		serverEntryTags := tx.bucket(datastoreServerEntryTagsBucket)
   284  		serverEntryTombstoneTags := tx.bucket(datastoreServerEntryTombstoneTagsBucket)
   285  
   286  		serverEntryID := []byte(serverEntryFields.GetIPAddress())
   287  
   288  		// Check not only that the entry exists, but is valid. This
   289  		// will replace in the rare case where the data is corrupt.
   290  		existingConfigurationVersion := -1
   291  		existingData := serverEntries.get(serverEntryID)
   292  		if existingData != nil {
   293  			var existingServerEntry *protocol.ServerEntry
   294  			err := json.Unmarshal(existingData, &existingServerEntry)
   295  			if err == nil {
   296  				existingConfigurationVersion = existingServerEntry.ConfigurationVersion
   297  			}
   298  		}
   299  
   300  		exists := existingConfigurationVersion > -1
   301  		newer := exists && existingConfigurationVersion < serverEntryFields.GetConfigurationVersion()
   302  		update := !exists || replaceIfExists || newer
   303  
   304  		if !update {
   305  			return nil
   306  		}
   307  
   308  		serverEntryTag := serverEntryFields.GetTag()
   309  
   310  		// Generate a derived tag when the server entry has no tag.
   311  		if serverEntryTag == "" {
   312  
   313  			serverEntryTag = protocol.GenerateServerEntryTag(
   314  				serverEntryFields.GetIPAddress(),
   315  				serverEntryFields.GetWebServerSecret())
   316  
   317  			serverEntryFields.SetTag(serverEntryTag)
   318  		}
   319  
   320  		serverEntryTagBytes := []byte(serverEntryTag)
   321  
   322  		// Ignore the server entry if it was previously pruned and a tombstone is
   323  		// set.
   324  		//
   325  		// This logic is enforced only for embedded server entries, as all other
   326  		// sources are considered to be definitive and non-stale. These exceptions
   327  		// intentionally allow the scenario where a server is temporarily deleted
   328  		// and then restored; in this case, it's desired for pruned server entries
   329  		// to be restored.
   330  		if serverEntryFields.GetLocalSource() == protocol.SERVER_ENTRY_SOURCE_EMBEDDED {
   331  			if serverEntryTombstoneTags.get(serverEntryTagBytes) != nil {
   332  				return nil
   333  			}
   334  		}
   335  
   336  		data, err := json.Marshal(serverEntryFields)
   337  		if err != nil {
   338  			return errors.Trace(err)
   339  		}
   340  
   341  		err = serverEntries.put(serverEntryID, data)
   342  		if err != nil {
   343  			return errors.Trace(err)
   344  		}
   345  
   346  		err = serverEntryTags.put(serverEntryTagBytes, serverEntryID)
   347  		if err != nil {
   348  			return errors.Trace(err)
   349  		}
   350  
   351  		NoticeInfo("updated server %s", serverEntryFields.GetDiagnosticID())
   352  
   353  		return nil
   354  	})
   355  	if err != nil {
   356  		return errors.Trace(err)
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  // StoreServerEntries stores a list of server entries.
   363  // There is an independent transaction for each entry insert/update.
   364  func StoreServerEntries(
   365  	config *Config,
   366  	serverEntries []protocol.ServerEntryFields,
   367  	replaceIfExists bool) error {
   368  
   369  	for _, serverEntryFields := range serverEntries {
   370  		err := StoreServerEntry(serverEntryFields, replaceIfExists)
   371  		if err != nil {
   372  			return errors.Trace(err)
   373  		}
   374  	}
   375  
   376  	return nil
   377  }
   378  
   379  // StreamingStoreServerEntries stores a list of server entries. There is an
   380  // independent transaction for each entry insert/update.
   381  // StreamingStoreServerEntries stops early and returns an error if ctx becomes
   382  // done; any server entries stored up to that point are retained.
   383  func StreamingStoreServerEntries(
   384  	ctx context.Context,
   385  	config *Config,
   386  	serverEntries *protocol.StreamingServerEntryDecoder,
   387  	replaceIfExists bool) error {
   388  
   389  	// Note: both StreamingServerEntryDecoder.Next and StoreServerEntry
   390  	// allocate temporary memory buffers for hex/JSON decoding/encoding,
   391  	// so this isn't true constant-memory streaming (it depends on garbage
   392  	// collection).
   393  
   394  	n := 0
   395  	for {
   396  
   397  		select {
   398  		case <-ctx.Done():
   399  			return errors.Trace(ctx.Err())
   400  		default:
   401  		}
   402  
   403  		serverEntry, err := serverEntries.Next()
   404  		if err != nil {
   405  			return errors.Trace(err)
   406  		}
   407  
   408  		if serverEntry == nil {
   409  			// No more server entries
   410  			return nil
   411  		}
   412  
   413  		err = StoreServerEntry(serverEntry, replaceIfExists)
   414  		if err != nil {
   415  			return errors.Trace(err)
   416  		}
   417  
   418  		n += 1
   419  		if n == datastoreServerEntryFetchGCThreshold {
   420  			DoGarbageCollection()
   421  			n = 0
   422  		}
   423  	}
   424  
   425  	return nil
   426  }
   427  
   428  // ImportEmbeddedServerEntries loads, decodes, and stores a list of server
   429  // entries. If embeddedServerEntryListFilename is not empty,
   430  // embeddedServerEntryList will be ignored and the encoded server entry list
   431  // will be loaded from the specified file. The import process stops early if
   432  // ctx becomes done; any server entries imported up to that point are
   433  // retained.
   434  func ImportEmbeddedServerEntries(
   435  	ctx context.Context,
   436  	config *Config,
   437  	embeddedServerEntryListFilename string,
   438  	embeddedServerEntryList string) error {
   439  
   440  	var reader io.Reader
   441  
   442  	if embeddedServerEntryListFilename != "" {
   443  
   444  		file, err := os.Open(embeddedServerEntryListFilename)
   445  		if err != nil {
   446  			return errors.Trace(err)
   447  		}
   448  		defer file.Close()
   449  
   450  		reader = file
   451  
   452  	} else {
   453  
   454  		reader = strings.NewReader(embeddedServerEntryList)
   455  	}
   456  
   457  	err := StreamingStoreServerEntries(
   458  		ctx,
   459  		config,
   460  		protocol.NewStreamingServerEntryDecoder(
   461  			reader,
   462  			common.TruncateTimestampToHour(common.GetCurrentTimestamp()),
   463  			protocol.SERVER_ENTRY_SOURCE_EMBEDDED),
   464  		false)
   465  	if err != nil {
   466  		return errors.Trace(err)
   467  	}
   468  
   469  	return nil
   470  }
   471  
   472  // PromoteServerEntry sets the server affinity server entry ID to the
   473  // specified server entry IP address.
   474  func PromoteServerEntry(config *Config, ipAddress string) error {
   475  	err := datastoreUpdate(func(tx *datastoreTx) error {
   476  
   477  		serverEntryID := []byte(ipAddress)
   478  
   479  		// Ensure the corresponding server entry exists before
   480  		// setting server affinity.
   481  		bucket := tx.bucket(datastoreServerEntriesBucket)
   482  		data := bucket.get(serverEntryID)
   483  		if data == nil {
   484  			NoticeWarning(
   485  				"PromoteServerEntry: ignoring unknown server entry: %s",
   486  				ipAddress)
   487  			return nil
   488  		}
   489  
   490  		bucket = tx.bucket(datastoreKeyValueBucket)
   491  		err := bucket.put(datastoreAffinityServerEntryIDKey, serverEntryID)
   492  		if err != nil {
   493  			return errors.Trace(err)
   494  		}
   495  
   496  		// Store the current server entry filter (e.g, region, etc.) that
   497  		// was in use when the entry was promoted. This is used to detect
   498  		// when the top ranked server entry was promoted under a different
   499  		// filter.
   500  
   501  		currentFilter, err := makeServerEntryFilterValue(config)
   502  		if err != nil {
   503  			return errors.Trace(err)
   504  		}
   505  
   506  		err = bucket.put(datastoreLastServerEntryFilterKey, currentFilter)
   507  		if err != nil {
   508  			return errors.Trace(err)
   509  		}
   510  
   511  		return nil
   512  	})
   513  
   514  	if err != nil {
   515  		return errors.Trace(err)
   516  	}
   517  	return nil
   518  }
   519  
   520  // DeleteServerEntryAffinity clears server affinity if set to the specified
   521  // server.
   522  func DeleteServerEntryAffinity(ipAddress string) error {
   523  	err := datastoreUpdate(func(tx *datastoreTx) error {
   524  
   525  		serverEntryID := []byte(ipAddress)
   526  
   527  		bucket := tx.bucket(datastoreKeyValueBucket)
   528  
   529  		affinityServerEntryID := bucket.get(datastoreAffinityServerEntryIDKey)
   530  
   531  		if bytes.Equal(affinityServerEntryID, serverEntryID) {
   532  			err := bucket.delete(datastoreAffinityServerEntryIDKey)
   533  			if err != nil {
   534  				return errors.Trace(err)
   535  			}
   536  			err = bucket.delete(datastoreLastServerEntryFilterKey)
   537  			if err != nil {
   538  				return errors.Trace(err)
   539  			}
   540  		}
   541  
   542  		return nil
   543  	})
   544  
   545  	if err != nil {
   546  		return errors.Trace(err)
   547  	}
   548  	return nil
   549  }
   550  
   551  func makeServerEntryFilterValue(config *Config) ([]byte, error) {
   552  
   553  	// Currently, only a change of EgressRegion will "break" server affinity.
   554  	// If the tunnel protocol filter changes, any existing affinity server
   555  	// either passes the new filter, or it will be skipped anyway.
   556  
   557  	return []byte(config.EgressRegion), nil
   558  }
   559  
   560  func hasServerEntryFilterChanged(config *Config) (bool, error) {
   561  
   562  	currentFilter, err := makeServerEntryFilterValue(config)
   563  	if err != nil {
   564  		return false, errors.Trace(err)
   565  	}
   566  
   567  	changed := false
   568  	err = datastoreView(func(tx *datastoreTx) error {
   569  
   570  		bucket := tx.bucket(datastoreKeyValueBucket)
   571  		previousFilter := bucket.get(datastoreLastServerEntryFilterKey)
   572  
   573  		// When not found, previousFilter will be nil; ensures this
   574  		// results in "changed", even if currentFilter is len(0).
   575  		if previousFilter == nil ||
   576  			!bytes.Equal(previousFilter, currentFilter) {
   577  			changed = true
   578  		}
   579  		return nil
   580  	})
   581  	if err != nil {
   582  		return false, errors.Trace(err)
   583  	}
   584  
   585  	return changed, nil
   586  }
   587  
   588  // ServerEntryIterator is used to iterate over
   589  // stored server entries in rank order.
   590  type ServerEntryIterator struct {
   591  	config                       *Config
   592  	applyServerAffinity          bool
   593  	serverEntryIDs               [][]byte
   594  	serverEntryIndex             int
   595  	isTacticsServerEntryIterator bool
   596  	isTargetServerEntryIterator  bool
   597  	hasNextTargetServerEntry     bool
   598  	targetServerEntry            *protocol.ServerEntry
   599  }
   600  
   601  // NewServerEntryIterator creates a new ServerEntryIterator.
   602  //
   603  // The boolean return value indicates whether to treat the first server(s)
   604  // as affinity servers or not. When the server entry selection filter changes
   605  // such as from a specific region to any region, or when there was no previous
   606  // filter/iterator, the the first server(s) are arbitrary and should not be
   607  // given affinity treatment.
   608  //
   609  // NewServerEntryIterator and any returned ServerEntryIterator are not
   610  // designed for concurrent use as not all related datastore operations are
   611  // performed in a single transaction.
   612  //
   613  func NewServerEntryIterator(config *Config) (bool, *ServerEntryIterator, error) {
   614  
   615  	// When configured, this target server entry is the only candidate
   616  	if config.TargetServerEntry != "" {
   617  		return newTargetServerEntryIterator(config, false)
   618  	}
   619  
   620  	filterChanged, err := hasServerEntryFilterChanged(config)
   621  	if err != nil {
   622  		return false, nil, errors.Trace(err)
   623  	}
   624  
   625  	applyServerAffinity := !filterChanged
   626  
   627  	iterator := &ServerEntryIterator{
   628  		config:              config,
   629  		applyServerAffinity: applyServerAffinity,
   630  	}
   631  
   632  	err = iterator.reset(true)
   633  	if err != nil {
   634  		return false, nil, errors.Trace(err)
   635  	}
   636  
   637  	return applyServerAffinity, iterator, nil
   638  }
   639  
   640  func NewTacticsServerEntryIterator(config *Config) (*ServerEntryIterator, error) {
   641  
   642  	// When configured, this target server entry is the only candidate
   643  	if config.TargetServerEntry != "" {
   644  		_, iterator, err := newTargetServerEntryIterator(config, true)
   645  		return iterator, err
   646  	}
   647  
   648  	iterator := &ServerEntryIterator{
   649  		config:                       config,
   650  		isTacticsServerEntryIterator: true,
   651  	}
   652  
   653  	err := iterator.reset(true)
   654  	if err != nil {
   655  		return nil, errors.Trace(err)
   656  	}
   657  
   658  	return iterator, nil
   659  }
   660  
   661  // newTargetServerEntryIterator is a helper for initializing the TargetServerEntry case
   662  func newTargetServerEntryIterator(config *Config, isTactics bool) (bool, *ServerEntryIterator, error) {
   663  
   664  	serverEntry, err := protocol.DecodeServerEntry(
   665  		config.TargetServerEntry, config.loadTimestamp, protocol.SERVER_ENTRY_SOURCE_TARGET)
   666  	if err != nil {
   667  		return false, nil, errors.Trace(err)
   668  	}
   669  
   670  	if serverEntry.Tag == "" {
   671  		serverEntry.Tag = protocol.GenerateServerEntryTag(
   672  			serverEntry.IpAddress, serverEntry.WebServerSecret)
   673  	}
   674  
   675  	if isTactics {
   676  
   677  		if len(serverEntry.GetSupportedTacticsProtocols()) == 0 {
   678  			return false, nil, errors.TraceNew("TargetServerEntry does not support tactics protocols")
   679  		}
   680  
   681  	} else {
   682  
   683  		if config.EgressRegion != "" && serverEntry.Region != config.EgressRegion {
   684  			return false, nil, errors.TraceNew("TargetServerEntry does not support EgressRegion")
   685  		}
   686  
   687  		p := config.GetParameters().Get()
   688  		limitTunnelProtocols := p.TunnelProtocols(parameters.LimitTunnelProtocols)
   689  		limitTunnelDialPortNumbers := protocol.TunnelProtocolPortLists(
   690  			p.TunnelProtocolPortLists(parameters.LimitTunnelDialPortNumbers))
   691  		limitQUICVersions := p.QUICVersions(parameters.LimitQUICVersions)
   692  
   693  		if len(limitTunnelProtocols) > 0 {
   694  			// At the ServerEntryIterator level, only limitTunnelProtocols is applied;
   695  			// excludeIntensive is handled higher up.
   696  			if len(serverEntry.GetSupportedProtocols(
   697  				conditionallyEnabledComponents{},
   698  				config.UseUpstreamProxy(),
   699  				limitTunnelProtocols,
   700  				limitTunnelDialPortNumbers,
   701  				limitQUICVersions,
   702  				false)) == 0 {
   703  				return false, nil, errors.Tracef(
   704  					"TargetServerEntry does not support LimitTunnelProtocols: %v", limitTunnelProtocols)
   705  			}
   706  		}
   707  	}
   708  
   709  	iterator := &ServerEntryIterator{
   710  		isTacticsServerEntryIterator: isTactics,
   711  		isTargetServerEntryIterator:  true,
   712  		hasNextTargetServerEntry:     true,
   713  		targetServerEntry:            serverEntry,
   714  	}
   715  
   716  	NoticeInfo("using TargetServerEntry: %s", serverEntry.GetDiagnosticID())
   717  
   718  	return false, iterator, nil
   719  }
   720  
   721  // Reset a NewServerEntryIterator to the start of its cycle. The next
   722  // call to Next will return the first server entry.
   723  func (iterator *ServerEntryIterator) Reset() error {
   724  	return iterator.reset(false)
   725  }
   726  
   727  func (iterator *ServerEntryIterator) reset(isInitialRound bool) error {
   728  	iterator.Close()
   729  
   730  	if iterator.isTargetServerEntryIterator {
   731  		iterator.hasNextTargetServerEntry = true
   732  		return nil
   733  	}
   734  
   735  	// Support stand-alone GetTactics operation. See TacticsStorer for more
   736  	// details.
   737  	if iterator.isTacticsServerEntryIterator {
   738  		err := OpenDataStoreWithoutRetry(iterator.config)
   739  		if err != nil {
   740  			return errors.Trace(err)
   741  		}
   742  		defer CloseDataStore()
   743  	}
   744  
   745  	// BoltDB implementation note:
   746  	// We don't keep a transaction open for the duration of the iterator
   747  	// because this would expose the following semantics to consumer code:
   748  	//
   749  	//     Read-only transactions and read-write transactions ... generally
   750  	//     shouldn't be opened simultaneously in the same goroutine. This can
   751  	//     cause a deadlock as the read-write transaction needs to periodically
   752  	//     re-map the data file but it cannot do so while a read-only
   753  	//     transaction is open.
   754  	//     (https://github.com/boltdb/bolt)
   755  	//
   756  	// So the underlying serverEntriesBucket could change after the serverEntryIDs
   757  	// list is built.
   758  
   759  	var serverEntryIDs [][]byte
   760  
   761  	err := datastoreView(func(tx *datastoreTx) error {
   762  
   763  		bucket := tx.bucket(datastoreKeyValueBucket)
   764  
   765  		serverEntryIDs = make([][]byte, 0)
   766  		shuffleHead := 0
   767  
   768  		var affinityServerEntryID []byte
   769  
   770  		// In the first round only, move any server affinity candiate to the
   771  		// very first position.
   772  
   773  		if isInitialRound &&
   774  			iterator.applyServerAffinity {
   775  
   776  			affinityServerEntryID = bucket.get(datastoreAffinityServerEntryIDKey)
   777  			if affinityServerEntryID != nil {
   778  				serverEntryIDs = append(serverEntryIDs, append([]byte(nil), affinityServerEntryID...))
   779  				shuffleHead = 1
   780  			}
   781  		}
   782  
   783  		bucket = tx.bucket(datastoreServerEntriesBucket)
   784  		cursor := bucket.cursor()
   785  		for key := cursor.firstKey(); key != nil; key = cursor.nextKey() {
   786  			if affinityServerEntryID != nil {
   787  				if bytes.Equal(affinityServerEntryID, key) {
   788  					continue
   789  				}
   790  			}
   791  			serverEntryIDs = append(serverEntryIDs, append([]byte(nil), key...))
   792  		}
   793  		cursor.close()
   794  
   795  		// Randomly shuffle the entire list of server IDs, excluding the
   796  		// server affinity candidate.
   797  
   798  		for i := len(serverEntryIDs) - 1; i > shuffleHead-1; i-- {
   799  			j := prng.Intn(i+1-shuffleHead) + shuffleHead
   800  			serverEntryIDs[i], serverEntryIDs[j] = serverEntryIDs[j], serverEntryIDs[i]
   801  		}
   802  
   803  		// In the first round, or with some probability, move _potential_ replay
   804  		// candidates to the front of the list (excepting the server affinity slot,
   805  		// if any). This move is post-shuffle so the order is still randomized. To
   806  		// save the memory overhead of unmarshalling all dial parameters, this
   807  		// operation just moves any server with a dial parameter record to the
   808  		// front. Whether the dial parameter remains valid for replay -- TTL,
   809  		// tactics/config unchanged, etc. --- is checked later.
   810  		//
   811  		// TODO: move only up to parameters.ReplayCandidateCount to front?
   812  
   813  		p := iterator.config.GetParameters().Get()
   814  
   815  		if (isInitialRound || p.WeightedCoinFlip(parameters.ReplayLaterRoundMoveToFrontProbability)) &&
   816  			p.Int(parameters.ReplayCandidateCount) != 0 {
   817  
   818  			networkID := []byte(iterator.config.GetNetworkID())
   819  
   820  			dialParamsBucket := tx.bucket(datastoreDialParametersBucket)
   821  			i := shuffleHead
   822  			j := len(serverEntryIDs) - 1
   823  			for {
   824  				for ; i < j; i++ {
   825  					key := makeDialParametersKey(serverEntryIDs[i], networkID)
   826  					if dialParamsBucket.get(key) == nil {
   827  						break
   828  					}
   829  				}
   830  				for ; i < j; j-- {
   831  					key := makeDialParametersKey(serverEntryIDs[j], networkID)
   832  					if dialParamsBucket.get(key) != nil {
   833  						break
   834  					}
   835  				}
   836  				if i < j {
   837  					serverEntryIDs[i], serverEntryIDs[j] = serverEntryIDs[j], serverEntryIDs[i]
   838  					i++
   839  					j--
   840  				} else {
   841  					break
   842  				}
   843  			}
   844  		}
   845  
   846  		return nil
   847  	})
   848  	if err != nil {
   849  		return errors.Trace(err)
   850  	}
   851  
   852  	iterator.serverEntryIDs = serverEntryIDs
   853  	iterator.serverEntryIndex = 0
   854  
   855  	return nil
   856  }
   857  
   858  // Close cleans up resources associated with a ServerEntryIterator.
   859  func (iterator *ServerEntryIterator) Close() {
   860  	iterator.serverEntryIDs = nil
   861  	iterator.serverEntryIndex = 0
   862  }
   863  
   864  // Next returns the next server entry, by rank, for a ServerEntryIterator.
   865  // Returns nil with no error when there is no next item.
   866  func (iterator *ServerEntryIterator) Next() (*protocol.ServerEntry, error) {
   867  
   868  	var serverEntry *protocol.ServerEntry
   869  	var err error
   870  
   871  	defer func() {
   872  		if err != nil {
   873  			iterator.Close()
   874  		}
   875  	}()
   876  
   877  	if iterator.isTargetServerEntryIterator {
   878  		if iterator.hasNextTargetServerEntry {
   879  			iterator.hasNextTargetServerEntry = false
   880  			return MakeCompatibleServerEntry(iterator.targetServerEntry), nil
   881  		}
   882  		return nil, nil
   883  	}
   884  
   885  	// Support stand-alone GetTactics operation. See TacticsStorer for more
   886  	// details.
   887  	if iterator.isTacticsServerEntryIterator {
   888  		err := OpenDataStoreWithoutRetry(iterator.config)
   889  		if err != nil {
   890  			return nil, errors.Trace(err)
   891  		}
   892  		defer CloseDataStore()
   893  	}
   894  
   895  	// There are no region/protocol indexes for the server entries bucket.
   896  	// Loop until we have the next server entry that matches the iterator
   897  	// filter requirements.
   898  	for {
   899  		if iterator.serverEntryIndex >= len(iterator.serverEntryIDs) {
   900  			// There is no next item
   901  			return nil, nil
   902  		}
   903  
   904  		serverEntryID := iterator.serverEntryIDs[iterator.serverEntryIndex]
   905  		iterator.serverEntryIndex += 1
   906  
   907  		serverEntry = nil
   908  		doDeleteServerEntry := false
   909  
   910  		err = datastoreView(func(tx *datastoreTx) error {
   911  			serverEntries := tx.bucket(datastoreServerEntriesBucket)
   912  			value := serverEntries.get(serverEntryID)
   913  			if value == nil {
   914  				return nil
   915  			}
   916  
   917  			// When the server entry has a signature and the signature verification
   918  			// public key is configured, perform a signature verification, which will
   919  			// detect data corruption of most server entry fields. When the check
   920  			// fails, the server entry is deleted and skipped and iteration continues.
   921  			//
   922  			// This prevents wasteful, time-consuming dials in cases where the server
   923  			// entry is intact except for a bit flip in the obfuscation key, for
   924  			// example. A delete is triggered also in the case where the server entry
   925  			// record fails to unmarshal.
   926  
   927  			if iterator.config.ServerEntrySignaturePublicKey != "" {
   928  
   929  				var serverEntryFields protocol.ServerEntryFields
   930  				err = json.Unmarshal(value, &serverEntryFields)
   931  				if err != nil {
   932  					doDeleteServerEntry = true
   933  					NoticeWarning(
   934  						"ServerEntryIterator.Next: unmarshal failed: %s",
   935  						errors.Trace(err))
   936  
   937  					// Do not stop iterating.
   938  					return nil
   939  				}
   940  
   941  				if serverEntryFields.HasSignature() {
   942  					err = serverEntryFields.VerifySignature(
   943  						iterator.config.ServerEntrySignaturePublicKey)
   944  					if err != nil {
   945  						doDeleteServerEntry = true
   946  						NoticeWarning(
   947  							"ServerEntryIterator.Next: verify signature failed: %s",
   948  							errors.Trace(err))
   949  
   950  						// Do not stop iterating.
   951  						return nil
   952  					}
   953  				}
   954  			}
   955  
   956  			// Must unmarshal here as slice is only valid within transaction.
   957  			err = json.Unmarshal(value, &serverEntry)
   958  
   959  			if err != nil {
   960  				serverEntry = nil
   961  				doDeleteServerEntry = true
   962  				NoticeWarning(
   963  					"ServerEntryIterator.Next: unmarshal failed: %s",
   964  					errors.Trace(err))
   965  
   966  				// Do not stop iterating.
   967  				return nil
   968  			}
   969  
   970  			return nil
   971  		})
   972  		if err != nil {
   973  			return nil, errors.Trace(err)
   974  		}
   975  
   976  		if doDeleteServerEntry {
   977  			deleteServerEntry(iterator.config, serverEntryID)
   978  			continue
   979  		}
   980  
   981  		if serverEntry == nil {
   982  			// In case of data corruption or a bug causing this condition,
   983  			// do not stop iterating.
   984  			NoticeWarning("ServerEntryIterator.Next: unexpected missing server entry")
   985  			continue
   986  		}
   987  
   988  		// Generate a derived server entry tag for server entries with no tag. Store
   989  		// back the updated server entry so that (a) the tag doesn't need to be
   990  		// regenerated; (b) the server entry can be looked up by tag (currently used
   991  		// in the status request prune case).
   992  		//
   993  		// This is a distinct transaction so as to avoid the overhead of regular
   994  		// write transactions in the iterator; once tags have been stored back, most
   995  		// iterator transactions will remain read-only.
   996  		if serverEntry.Tag == "" {
   997  
   998  			serverEntry.Tag = protocol.GenerateServerEntryTag(
   999  				serverEntry.IpAddress, serverEntry.WebServerSecret)
  1000  
  1001  			err = datastoreUpdate(func(tx *datastoreTx) error {
  1002  
  1003  				serverEntries := tx.bucket(datastoreServerEntriesBucket)
  1004  				serverEntryTags := tx.bucket(datastoreServerEntryTagsBucket)
  1005  
  1006  				// We must reload and store back the server entry _fields_ to preserve any
  1007  				// currently unrecognized fields, for future compatibility.
  1008  
  1009  				value := serverEntries.get(serverEntryID)
  1010  				if value == nil {
  1011  					return nil
  1012  				}
  1013  
  1014  				var serverEntryFields protocol.ServerEntryFields
  1015  				err := json.Unmarshal(value, &serverEntryFields)
  1016  				if err != nil {
  1017  					return errors.Trace(err)
  1018  				}
  1019  
  1020  				// As there is minor race condition between loading/checking serverEntry
  1021  				// and reloading/modifying serverEntryFields, this transaction references
  1022  				// only the freshly loaded fields when checking and setting the tag.
  1023  
  1024  				serverEntryTag := serverEntryFields.GetTag()
  1025  
  1026  				if serverEntryTag != "" {
  1027  					return nil
  1028  				}
  1029  
  1030  				serverEntryTag = protocol.GenerateServerEntryTag(
  1031  					serverEntryFields.GetIPAddress(),
  1032  					serverEntryFields.GetWebServerSecret())
  1033  
  1034  				serverEntryFields.SetTag(serverEntryTag)
  1035  
  1036  				jsonServerEntryFields, err := json.Marshal(serverEntryFields)
  1037  				if err != nil {
  1038  					return errors.Trace(err)
  1039  				}
  1040  
  1041  				serverEntries.put(serverEntryID, jsonServerEntryFields)
  1042  				if err != nil {
  1043  					return errors.Trace(err)
  1044  				}
  1045  
  1046  				serverEntryTags.put([]byte(serverEntryTag), serverEntryID)
  1047  				if err != nil {
  1048  					return errors.Trace(err)
  1049  				}
  1050  
  1051  				return nil
  1052  			})
  1053  
  1054  			if err != nil {
  1055  				// Do not stop.
  1056  				NoticeWarning(
  1057  					"ServerEntryIterator.Next: update server entry failed: %s",
  1058  					errors.Trace(err))
  1059  			}
  1060  		}
  1061  
  1062  		if iterator.serverEntryIndex%datastoreServerEntryFetchGCThreshold == 0 {
  1063  			DoGarbageCollection()
  1064  		}
  1065  
  1066  		// Check filter requirements
  1067  
  1068  		if iterator.isTacticsServerEntryIterator {
  1069  
  1070  			// Tactics doesn't filter by egress region.
  1071  			if len(serverEntry.GetSupportedTacticsProtocols()) > 0 {
  1072  				break
  1073  			}
  1074  
  1075  		} else {
  1076  
  1077  			if iterator.config.EgressRegion == "" ||
  1078  				serverEntry.Region == iterator.config.EgressRegion {
  1079  				break
  1080  			}
  1081  		}
  1082  	}
  1083  
  1084  	return MakeCompatibleServerEntry(serverEntry), nil
  1085  }
  1086  
  1087  // MakeCompatibleServerEntry provides backwards compatibility with old server entries
  1088  // which have a single meekFrontingDomain and not a meekFrontingAddresses array.
  1089  // By copying this one meekFrontingDomain into meekFrontingAddresses, this client effectively
  1090  // uses that single value as legacy clients do.
  1091  func MakeCompatibleServerEntry(serverEntry *protocol.ServerEntry) *protocol.ServerEntry {
  1092  	if len(serverEntry.MeekFrontingAddresses) == 0 && serverEntry.MeekFrontingDomain != "" {
  1093  		serverEntry.MeekFrontingAddresses =
  1094  			append(serverEntry.MeekFrontingAddresses, serverEntry.MeekFrontingDomain)
  1095  	}
  1096  
  1097  	return serverEntry
  1098  }
  1099  
  1100  // PruneServerEntry deletes the server entry, along with associated data,
  1101  // corresponding to the specified server entry tag. Pruning is subject to an
  1102  // age check. In the case of an error, a notice is emitted.
  1103  func PruneServerEntry(config *Config, serverEntryTag string) {
  1104  	err := pruneServerEntry(config, serverEntryTag)
  1105  	if err != nil {
  1106  		NoticeWarning(
  1107  			"PruneServerEntry failed: %s: %s",
  1108  			serverEntryTag, errors.Trace(err))
  1109  		return
  1110  	}
  1111  	NoticePruneServerEntry(serverEntryTag)
  1112  }
  1113  
  1114  func pruneServerEntry(config *Config, serverEntryTag string) error {
  1115  
  1116  	minimumAgeForPruning := config.GetParameters().Get().Duration(
  1117  		parameters.ServerEntryMinimumAgeForPruning)
  1118  
  1119  	return datastoreUpdate(func(tx *datastoreTx) error {
  1120  
  1121  		serverEntries := tx.bucket(datastoreServerEntriesBucket)
  1122  		serverEntryTags := tx.bucket(datastoreServerEntryTagsBucket)
  1123  		serverEntryTombstoneTags := tx.bucket(datastoreServerEntryTombstoneTagsBucket)
  1124  		keyValues := tx.bucket(datastoreKeyValueBucket)
  1125  		dialParameters := tx.bucket(datastoreDialParametersBucket)
  1126  
  1127  		serverEntryTagBytes := []byte(serverEntryTag)
  1128  
  1129  		serverEntryID := serverEntryTags.get(serverEntryTagBytes)
  1130  		if serverEntryID == nil {
  1131  			return errors.TraceNew("server entry tag not found")
  1132  		}
  1133  
  1134  		serverEntryJson := serverEntries.get(serverEntryID)
  1135  		if serverEntryJson == nil {
  1136  			return errors.TraceNew("server entry not found")
  1137  		}
  1138  
  1139  		var serverEntry *protocol.ServerEntry
  1140  		err := json.Unmarshal(serverEntryJson, &serverEntry)
  1141  		if err != nil {
  1142  			errors.Trace(err)
  1143  		}
  1144  
  1145  		// Only prune sufficiently old server entries. This mitigates the case where
  1146  		// stale data in psiphond will incorrectly identify brand new servers as
  1147  		// being invalid/deleted.
  1148  		serverEntryLocalTimestamp, err := time.Parse(time.RFC3339, serverEntry.LocalTimestamp)
  1149  		if err != nil {
  1150  			errors.Trace(err)
  1151  		}
  1152  		if serverEntryLocalTimestamp.Add(minimumAgeForPruning).After(time.Now()) {
  1153  			return nil
  1154  		}
  1155  
  1156  		// Handle the server IP recycle case where multiple serverEntryTags records
  1157  		// refer to the same server IP. Only delete the server entry record when its
  1158  		// tag matches the pruned tag. Otherwise, the server entry record is
  1159  		// associated with another tag. The pruned tag is still deleted.
  1160  		doDeleteServerEntry := (serverEntry.Tag == serverEntryTag)
  1161  
  1162  		err = serverEntryTags.delete(serverEntryTagBytes)
  1163  		if err != nil {
  1164  			errors.Trace(err)
  1165  		}
  1166  
  1167  		if doDeleteServerEntry {
  1168  
  1169  			err = deleteServerEntryHelper(
  1170  				config,
  1171  				serverEntryID,
  1172  				serverEntries,
  1173  				keyValues,
  1174  				dialParameters)
  1175  			if err != nil {
  1176  				errors.Trace(err)
  1177  			}
  1178  		}
  1179  
  1180  		// Tombstones prevent reimporting pruned server entries. Tombstone
  1181  		// identifiers are tags, which are derived from the web server secret in
  1182  		// addition to the server IP, so tombstones will not clobber recycled server
  1183  		// IPs as long as new web server secrets are generated in the recycle case.
  1184  		//
  1185  		// Tombstones are set only for embedded server entries, as all other sources
  1186  		// are expected to provide valid server entries; this also provides a fail-
  1187  		// safe mechanism to restore pruned server entries through all non-embedded
  1188  		// sources.
  1189  		if serverEntry.LocalSource == protocol.SERVER_ENTRY_SOURCE_EMBEDDED {
  1190  			err = serverEntryTombstoneTags.put(serverEntryTagBytes, []byte{1})
  1191  			if err != nil {
  1192  				return errors.Trace(err)
  1193  			}
  1194  		}
  1195  
  1196  		return nil
  1197  	})
  1198  }
  1199  
  1200  // DeleteServerEntry deletes the specified server entry and associated data.
  1201  func DeleteServerEntry(config *Config, ipAddress string) {
  1202  
  1203  	serverEntryID := []byte(ipAddress)
  1204  
  1205  	// For notices, we cannot assume we have a valid server entry tag value to
  1206  	// log, as DeleteServerEntry is called when a server entry fails to unmarshal
  1207  	// or fails signature verification.
  1208  
  1209  	err := deleteServerEntry(config, serverEntryID)
  1210  	if err != nil {
  1211  		NoticeWarning("DeleteServerEntry failed: %s", errors.Trace(err))
  1212  		return
  1213  	}
  1214  	NoticeInfo("Server entry deleted")
  1215  }
  1216  
  1217  func deleteServerEntry(config *Config, serverEntryID []byte) error {
  1218  
  1219  	return datastoreUpdate(func(tx *datastoreTx) error {
  1220  
  1221  		serverEntries := tx.bucket(datastoreServerEntriesBucket)
  1222  		serverEntryTags := tx.bucket(datastoreServerEntryTagsBucket)
  1223  		keyValues := tx.bucket(datastoreKeyValueBucket)
  1224  		dialParameters := tx.bucket(datastoreDialParametersBucket)
  1225  
  1226  		err := deleteServerEntryHelper(
  1227  			config,
  1228  			serverEntryID,
  1229  			serverEntries,
  1230  			keyValues,
  1231  			dialParameters)
  1232  		if err != nil {
  1233  			errors.Trace(err)
  1234  		}
  1235  
  1236  		// Remove any tags pointing to the deleted server entry.
  1237  		cursor := serverEntryTags.cursor()
  1238  		defer cursor.close()
  1239  		for key, value := cursor.first(); key != nil; key, value = cursor.next() {
  1240  			if bytes.Equal(value, serverEntryID) {
  1241  				err := serverEntryTags.delete(key)
  1242  				if err != nil {
  1243  					return errors.Trace(err)
  1244  				}
  1245  			}
  1246  		}
  1247  
  1248  		return nil
  1249  	})
  1250  }
  1251  
  1252  func deleteServerEntryHelper(
  1253  	config *Config,
  1254  	serverEntryID []byte,
  1255  	serverEntries *datastoreBucket,
  1256  	keyValues *datastoreBucket,
  1257  	dialParameters *datastoreBucket) error {
  1258  
  1259  	err := serverEntries.delete(serverEntryID)
  1260  	if err != nil {
  1261  		errors.Trace(err)
  1262  	}
  1263  
  1264  	affinityServerEntryID := keyValues.get(datastoreAffinityServerEntryIDKey)
  1265  	if bytes.Equal(affinityServerEntryID, serverEntryID) {
  1266  		err = keyValues.delete(datastoreAffinityServerEntryIDKey)
  1267  		if err != nil {
  1268  			return errors.Trace(err)
  1269  		}
  1270  		err = keyValues.delete(datastoreLastServerEntryFilterKey)
  1271  		if err != nil {
  1272  			return errors.Trace(err)
  1273  		}
  1274  	}
  1275  
  1276  	// TODO: expose boltdb Seek functionality to skip to first matching record.
  1277  	cursor := dialParameters.cursor()
  1278  	defer cursor.close()
  1279  	foundFirstMatch := false
  1280  	for key, _ := cursor.first(); key != nil; key, _ = cursor.next() {
  1281  		// Dial parameters key has serverID as a prefix; see makeDialParametersKey.
  1282  		if bytes.HasPrefix(key, serverEntryID) {
  1283  			foundFirstMatch = true
  1284  			err := dialParameters.delete(key)
  1285  			if err != nil {
  1286  				return errors.Trace(err)
  1287  			}
  1288  		} else if foundFirstMatch {
  1289  			break
  1290  		}
  1291  	}
  1292  
  1293  	return nil
  1294  }
  1295  
  1296  // ScanServerEntries iterates over all stored server entries, unmarshals each,
  1297  // and passes it to callback for processing. If callback returns false, the
  1298  // iteration is cancelled and an error is returned.
  1299  //
  1300  // ScanServerEntries may be slow to execute, particularly for older devices
  1301  // and/or very large server lists. Callers should avoid blocking on
  1302  // ScanServerEntries where possible; and use the canel option to interrupt
  1303  // scans that are no longer required.
  1304  func ScanServerEntries(callback func(*protocol.ServerEntry) bool) error {
  1305  
  1306  	// TODO: this operation can be sped up (by a factor of ~2x, in one test
  1307  	// scenario) by using a faster JSON implementation
  1308  	// (https://github.com/json-iterator/go) and increasing
  1309  	// datastoreServerEntryFetchGCThreshold.
  1310  	//
  1311  	// json-iterator increases the binary code size significantly, which affects
  1312  	// memory limit accounting on some platforms, so it's not clear we can use it
  1313  	// universally. Similarly, tuning datastoreServerEntryFetchGCThreshold has a
  1314  	// memory limit tradeoff.
  1315  	//
  1316  	// Since ScanServerEntries is now called asynchronously and doesn't block
  1317  	// establishment at all, we can tolerate its slower performance. Other
  1318  	// bulk-JSON operations such as [Streaming]StoreServerEntries also benefit
  1319  	// from using a faster JSON implementation, but the relative performance
  1320  	// increase is far smaller as import times are dominated by data store write
  1321  	// transaction overhead. Other operations such as ServerEntryIterator
  1322  	// amortize the cost of JSON unmarshalling over many other operations.
  1323  
  1324  	err := datastoreView(func(tx *datastoreTx) error {
  1325  
  1326  		bucket := tx.bucket(datastoreServerEntriesBucket)
  1327  		cursor := bucket.cursor()
  1328  		n := 0
  1329  
  1330  		for key, value := cursor.first(); key != nil; key, value = cursor.next() {
  1331  
  1332  			var serverEntry *protocol.ServerEntry
  1333  			err := json.Unmarshal(value, &serverEntry)
  1334  			if err != nil {
  1335  				// In case of data corruption or a bug causing this condition,
  1336  				// do not stop iterating.
  1337  				NoticeWarning("ScanServerEntries: %s", errors.Trace(err))
  1338  				continue
  1339  			}
  1340  
  1341  			if !callback(serverEntry) {
  1342  				cursor.close()
  1343  				return errors.TraceNew("scan cancelled")
  1344  			}
  1345  
  1346  			n += 1
  1347  			if n == datastoreServerEntryFetchGCThreshold {
  1348  				DoGarbageCollection()
  1349  				n = 0
  1350  			}
  1351  		}
  1352  		cursor.close()
  1353  		return nil
  1354  	})
  1355  
  1356  	if err != nil {
  1357  		return errors.Trace(err)
  1358  	}
  1359  
  1360  	return nil
  1361  }
  1362  
  1363  // HasServerEntries returns a bool indicating if the data store contains at
  1364  // least one server entry. This is a faster operation than CountServerEntries.
  1365  // On failure, HasServerEntries returns false.
  1366  func HasServerEntries() bool {
  1367  
  1368  	hasServerEntries := false
  1369  
  1370  	err := datastoreView(func(tx *datastoreTx) error {
  1371  		bucket := tx.bucket(datastoreServerEntriesBucket)
  1372  		cursor := bucket.cursor()
  1373  		key, _ := cursor.first()
  1374  		hasServerEntries = (key != nil)
  1375  		cursor.close()
  1376  		return nil
  1377  	})
  1378  
  1379  	if err != nil {
  1380  		NoticeWarning("HasServerEntries failed: %s", errors.Trace(err))
  1381  		return false
  1382  	}
  1383  
  1384  	return hasServerEntries
  1385  }
  1386  
  1387  // CountServerEntries returns a count of stored server entries. On failure,
  1388  // CountServerEntries returns 0.
  1389  func CountServerEntries() int {
  1390  
  1391  	count := 0
  1392  
  1393  	err := datastoreView(func(tx *datastoreTx) error {
  1394  		bucket := tx.bucket(datastoreServerEntriesBucket)
  1395  		cursor := bucket.cursor()
  1396  		for key, _ := cursor.first(); key != nil; key, _ = cursor.next() {
  1397  			count += 1
  1398  		}
  1399  		cursor.close()
  1400  		return nil
  1401  	})
  1402  
  1403  	if err != nil {
  1404  		NoticeWarning("CountServerEntries failed: %s", err)
  1405  		return 0
  1406  	}
  1407  
  1408  	return count
  1409  }
  1410  
  1411  // SetUrlETag stores an ETag for the specfied URL.
  1412  // Note: input URL is treated as a string, and is not
  1413  // encoded or decoded or otherwise canonicalized.
  1414  func SetUrlETag(url, etag string) error {
  1415  
  1416  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1417  		bucket := tx.bucket(datastoreUrlETagsBucket)
  1418  		err := bucket.put([]byte(url), []byte(etag))
  1419  		if err != nil {
  1420  			return errors.Trace(err)
  1421  		}
  1422  		return nil
  1423  	})
  1424  
  1425  	if err != nil {
  1426  		return errors.Trace(err)
  1427  	}
  1428  	return nil
  1429  }
  1430  
  1431  // GetUrlETag retrieves a previously stored an ETag for the
  1432  // specfied URL. If not found, it returns an empty string value.
  1433  func GetUrlETag(url string) (string, error) {
  1434  
  1435  	var etag string
  1436  
  1437  	err := datastoreView(func(tx *datastoreTx) error {
  1438  		bucket := tx.bucket(datastoreUrlETagsBucket)
  1439  		etag = string(bucket.get([]byte(url)))
  1440  		return nil
  1441  	})
  1442  
  1443  	if err != nil {
  1444  		return "", errors.Trace(err)
  1445  	}
  1446  	return etag, nil
  1447  }
  1448  
  1449  // SetKeyValue stores a key/value pair.
  1450  func SetKeyValue(key, value string) error {
  1451  
  1452  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1453  		bucket := tx.bucket(datastoreKeyValueBucket)
  1454  		err := bucket.put([]byte(key), []byte(value))
  1455  		if err != nil {
  1456  			return errors.Trace(err)
  1457  		}
  1458  		return nil
  1459  	})
  1460  
  1461  	if err != nil {
  1462  		return errors.Trace(err)
  1463  	}
  1464  	return nil
  1465  }
  1466  
  1467  // GetKeyValue retrieves the value for a given key. If not found,
  1468  // it returns an empty string value.
  1469  func GetKeyValue(key string) (string, error) {
  1470  
  1471  	var value string
  1472  
  1473  	err := datastoreView(func(tx *datastoreTx) error {
  1474  		bucket := tx.bucket(datastoreKeyValueBucket)
  1475  		value = string(bucket.get([]byte(key)))
  1476  		return nil
  1477  	})
  1478  
  1479  	if err != nil {
  1480  		return "", errors.Trace(err)
  1481  	}
  1482  	return value, nil
  1483  }
  1484  
  1485  // Persistent stat records in the persistentStatStateUnreported
  1486  // state are available for take out.
  1487  //
  1488  // Records in the persistentStatStateReporting have been taken
  1489  // out and are pending either deletion (for a successful request)
  1490  // or change to StateUnreported (for a failed request).
  1491  //
  1492  // All persistent stat records are reverted to StateUnreported
  1493  // when the datastore is initialized at start up.
  1494  
  1495  var persistentStatStateUnreported = []byte("0")
  1496  var persistentStatStateReporting = []byte("1")
  1497  
  1498  var persistentStatTypes = []string{
  1499  	datastorePersistentStatTypeRemoteServerList,
  1500  	datastorePersistentStatTypeFailedTunnel,
  1501  }
  1502  
  1503  // StorePersistentStat adds a new persistent stat record, which
  1504  // is set to StateUnreported and is an immediate candidate for
  1505  // reporting.
  1506  //
  1507  // The stat is a JSON byte array containing fields as
  1508  // required by the Psiphon server API. It's assumed that the
  1509  // JSON value contains enough unique information for the value to
  1510  // function as a key in the key/value datastore.
  1511  //
  1512  // Only up to PersistentStatsMaxStoreRecords are stored. Once this
  1513  // limit is reached, new records are discarded.
  1514  func StorePersistentStat(config *Config, statType string, stat []byte) error {
  1515  
  1516  	if !common.Contains(persistentStatTypes, statType) {
  1517  		return errors.Tracef("invalid persistent stat type: %s", statType)
  1518  	}
  1519  
  1520  	maxStoreRecords := config.GetParameters().Get().Int(
  1521  		parameters.PersistentStatsMaxStoreRecords)
  1522  
  1523  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1524  		bucket := tx.bucket([]byte(statType))
  1525  
  1526  		count := 0
  1527  		cursor := bucket.cursor()
  1528  		for key, _ := cursor.first(); key != nil; key, _ = cursor.next() {
  1529  			count++
  1530  		}
  1531  		cursor.close()
  1532  
  1533  		// TODO: assuming newer metrics are more useful, replace oldest record
  1534  		// instead of discarding?
  1535  
  1536  		if count >= maxStoreRecords {
  1537  			// Silently discard.
  1538  			return nil
  1539  		}
  1540  
  1541  		err := bucket.put(stat, persistentStatStateUnreported)
  1542  		if err != nil {
  1543  			return errors.Trace(err)
  1544  		}
  1545  
  1546  		return nil
  1547  	})
  1548  
  1549  	if err != nil {
  1550  		return errors.Trace(err)
  1551  	}
  1552  
  1553  	return nil
  1554  }
  1555  
  1556  // CountUnreportedPersistentStats returns the number of persistent
  1557  // stat records in StateUnreported.
  1558  func CountUnreportedPersistentStats() int {
  1559  
  1560  	unreported := 0
  1561  
  1562  	err := datastoreView(func(tx *datastoreTx) error {
  1563  
  1564  		for _, statType := range persistentStatTypes {
  1565  
  1566  			bucket := tx.bucket([]byte(statType))
  1567  			cursor := bucket.cursor()
  1568  			for key, value := cursor.first(); key != nil; key, value = cursor.next() {
  1569  				if bytes.Equal(value, persistentStatStateUnreported) {
  1570  					unreported++
  1571  				}
  1572  			}
  1573  			cursor.close()
  1574  		}
  1575  		return nil
  1576  	})
  1577  
  1578  	if err != nil {
  1579  		NoticeWarning("CountUnreportedPersistentStats failed: %s", err)
  1580  		return 0
  1581  	}
  1582  
  1583  	return unreported
  1584  }
  1585  
  1586  // TakeOutUnreportedPersistentStats returns persistent stats records that are
  1587  // in StateUnreported. At least one record, if present, will be returned and
  1588  // then additional records up to PersistentStatsMaxSendBytes. The records are
  1589  // set to StateReporting. If the records are successfully reported, clear them
  1590  // with ClearReportedPersistentStats. If the records are not successfully
  1591  // reported, restore them with PutBackUnreportedPersistentStats.
  1592  func TakeOutUnreportedPersistentStats(config *Config) (map[string][][]byte, error) {
  1593  
  1594  	stats := make(map[string][][]byte)
  1595  
  1596  	maxSendBytes := config.GetParameters().Get().Int(
  1597  		parameters.PersistentStatsMaxSendBytes)
  1598  
  1599  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1600  
  1601  		sendBytes := 0
  1602  
  1603  		for _, statType := range persistentStatTypes {
  1604  
  1605  			bucket := tx.bucket([]byte(statType))
  1606  			cursor := bucket.cursor()
  1607  			for key, value := cursor.first(); key != nil; key, value = cursor.next() {
  1608  
  1609  				// Perform a test JSON unmarshaling. In case of data corruption or a bug,
  1610  				// delete and skip the record.
  1611  				var jsonData interface{}
  1612  				err := json.Unmarshal(key, &jsonData)
  1613  				if err != nil {
  1614  					NoticeWarning(
  1615  						"Invalid key in TakeOutUnreportedPersistentStats: %s: %s",
  1616  						string(key), err)
  1617  					bucket.delete(key)
  1618  					continue
  1619  				}
  1620  
  1621  				if bytes.Equal(value, persistentStatStateUnreported) {
  1622  					// Must make a copy as slice is only valid within transaction.
  1623  					data := make([]byte, len(key))
  1624  					copy(data, key)
  1625  
  1626  					if stats[statType] == nil {
  1627  						stats[statType] = make([][]byte, 0)
  1628  					}
  1629  
  1630  					stats[statType] = append(stats[statType], data)
  1631  
  1632  					sendBytes += len(data)
  1633  					if sendBytes >= maxSendBytes {
  1634  						break
  1635  					}
  1636  				}
  1637  
  1638  			}
  1639  			cursor.close()
  1640  
  1641  			for _, key := range stats[statType] {
  1642  				err := bucket.put(key, persistentStatStateReporting)
  1643  				if err != nil {
  1644  					return errors.Trace(err)
  1645  				}
  1646  			}
  1647  
  1648  		}
  1649  		return nil
  1650  	})
  1651  
  1652  	if err != nil {
  1653  		return nil, errors.Trace(err)
  1654  	}
  1655  
  1656  	return stats, nil
  1657  }
  1658  
  1659  // PutBackUnreportedPersistentStats restores a list of persistent
  1660  // stat records to StateUnreported.
  1661  func PutBackUnreportedPersistentStats(stats map[string][][]byte) error {
  1662  
  1663  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1664  
  1665  		for _, statType := range persistentStatTypes {
  1666  
  1667  			bucket := tx.bucket([]byte(statType))
  1668  			for _, key := range stats[statType] {
  1669  				err := bucket.put(key, persistentStatStateUnreported)
  1670  				if err != nil {
  1671  					return errors.Trace(err)
  1672  				}
  1673  			}
  1674  		}
  1675  
  1676  		return nil
  1677  	})
  1678  
  1679  	if err != nil {
  1680  		return errors.Trace(err)
  1681  	}
  1682  
  1683  	return nil
  1684  }
  1685  
  1686  // ClearReportedPersistentStats deletes a list of persistent
  1687  // stat records that were successfully reported.
  1688  func ClearReportedPersistentStats(stats map[string][][]byte) error {
  1689  
  1690  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1691  
  1692  		for _, statType := range persistentStatTypes {
  1693  
  1694  			bucket := tx.bucket([]byte(statType))
  1695  			for _, key := range stats[statType] {
  1696  				err := bucket.delete(key)
  1697  				if err != nil {
  1698  					return err
  1699  				}
  1700  			}
  1701  		}
  1702  
  1703  		return nil
  1704  	})
  1705  
  1706  	if err != nil {
  1707  		return errors.Trace(err)
  1708  	}
  1709  
  1710  	return nil
  1711  }
  1712  
  1713  // resetAllPersistentStatsToUnreported sets all persistent stat
  1714  // records to StateUnreported. This reset is called when the
  1715  // datastore is initialized at start up, as we do not know if
  1716  // persistent records in StateReporting were reported or not.
  1717  func resetAllPersistentStatsToUnreported() error {
  1718  
  1719  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1720  
  1721  		for _, statType := range persistentStatTypes {
  1722  
  1723  			bucket := tx.bucket([]byte(statType))
  1724  			resetKeys := make([][]byte, 0)
  1725  			cursor := bucket.cursor()
  1726  			for key := cursor.firstKey(); key != nil; key = cursor.nextKey() {
  1727  				resetKeys = append(resetKeys, key)
  1728  			}
  1729  			cursor.close()
  1730  			// TODO: data mutation is done outside cursor. Is this
  1731  			// strictly necessary in this case? As is, this means
  1732  			// all stats need to be loaded into memory at once.
  1733  			// https://godoc.org/github.com/boltdb/bolt#Cursor
  1734  			for _, key := range resetKeys {
  1735  				err := bucket.put(key, persistentStatStateUnreported)
  1736  				if err != nil {
  1737  					return errors.Trace(err)
  1738  				}
  1739  			}
  1740  		}
  1741  
  1742  		return nil
  1743  	})
  1744  
  1745  	if err != nil {
  1746  		return errors.Trace(err)
  1747  	}
  1748  
  1749  	return nil
  1750  }
  1751  
  1752  // CountSLOKs returns the total number of SLOK records.
  1753  func CountSLOKs() int {
  1754  
  1755  	count := 0
  1756  
  1757  	err := datastoreView(func(tx *datastoreTx) error {
  1758  		bucket := tx.bucket(datastoreSLOKsBucket)
  1759  		cursor := bucket.cursor()
  1760  		for key := cursor.firstKey(); key != nil; key = cursor.nextKey() {
  1761  			count++
  1762  		}
  1763  		cursor.close()
  1764  		return nil
  1765  	})
  1766  
  1767  	if err != nil {
  1768  		NoticeWarning("CountSLOKs failed: %s", err)
  1769  		return 0
  1770  	}
  1771  
  1772  	return count
  1773  }
  1774  
  1775  // DeleteSLOKs deletes all SLOK records.
  1776  func DeleteSLOKs() error {
  1777  
  1778  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1779  		return tx.clearBucket(datastoreSLOKsBucket)
  1780  	})
  1781  
  1782  	if err != nil {
  1783  		return errors.Trace(err)
  1784  	}
  1785  
  1786  	return nil
  1787  }
  1788  
  1789  // SetSLOK stores a SLOK key, referenced by its ID. The bool
  1790  // return value indicates whether the SLOK was already stored.
  1791  func SetSLOK(id, slok []byte) (bool, error) {
  1792  
  1793  	var duplicate bool
  1794  
  1795  	err := datastoreUpdate(func(tx *datastoreTx) error {
  1796  		bucket := tx.bucket(datastoreSLOKsBucket)
  1797  		duplicate = bucket.get(id) != nil
  1798  		err := bucket.put(id, slok)
  1799  		if err != nil {
  1800  			return errors.Trace(err)
  1801  		}
  1802  		return nil
  1803  	})
  1804  
  1805  	if err != nil {
  1806  		return false, errors.Trace(err)
  1807  	}
  1808  
  1809  	return duplicate, nil
  1810  }
  1811  
  1812  // GetSLOK returns a SLOK key for the specified ID. The return
  1813  // value is nil if the SLOK is not found.
  1814  func GetSLOK(id []byte) ([]byte, error) {
  1815  
  1816  	var slok []byte
  1817  
  1818  	err := datastoreView(func(tx *datastoreTx) error {
  1819  		bucket := tx.bucket(datastoreSLOKsBucket)
  1820  		value := bucket.get(id)
  1821  		if value != nil {
  1822  			// Must make a copy as slice is only valid within transaction.
  1823  			slok = make([]byte, len(value))
  1824  			copy(slok, value)
  1825  		}
  1826  		return nil
  1827  	})
  1828  
  1829  	if err != nil {
  1830  		return nil, errors.Trace(err)
  1831  	}
  1832  
  1833  	return slok, nil
  1834  }
  1835  
  1836  func makeDialParametersKey(serverIPAddress, networkID []byte) []byte {
  1837  	// TODO: structured key?
  1838  	return append(append([]byte(nil), serverIPAddress...), networkID...)
  1839  }
  1840  
  1841  // SetDialParameters stores dial parameters associated with the specified
  1842  // server/network ID.
  1843  func SetDialParameters(serverIPAddress, networkID string, dialParams *DialParameters) error {
  1844  
  1845  	key := makeDialParametersKey([]byte(serverIPAddress), []byte(networkID))
  1846  
  1847  	data, err := json.Marshal(dialParams)
  1848  	if err != nil {
  1849  		return errors.Trace(err)
  1850  	}
  1851  
  1852  	return setBucketValue(datastoreDialParametersBucket, key, data)
  1853  }
  1854  
  1855  // GetDialParameters fetches any dial parameters associated with the specified
  1856  // server/network ID. Returns nil, nil when no record is found.
  1857  func GetDialParameters(
  1858  	config *Config, serverIPAddress, networkID string) (*DialParameters, error) {
  1859  
  1860  	// Support stand-alone GetTactics operation. See TacticsStorer for more
  1861  	// details.
  1862  	err := OpenDataStoreWithoutRetry(config)
  1863  	if err != nil {
  1864  		return nil, errors.Trace(err)
  1865  	}
  1866  	defer CloseDataStore()
  1867  
  1868  	key := makeDialParametersKey([]byte(serverIPAddress), []byte(networkID))
  1869  
  1870  	var dialParams *DialParameters
  1871  
  1872  	err = getBucketValue(
  1873  		datastoreDialParametersBucket,
  1874  		key,
  1875  		func(value []byte) error {
  1876  			if value == nil {
  1877  				return nil
  1878  			}
  1879  
  1880  			// Note: unlike with server entries, this record is not deleted when the
  1881  			// unmarshal fails, as the caller should proceed with the dial without dial
  1882  			// parameters; and when when the dial succeeds, new dial parameters will be
  1883  			// written over this record.
  1884  
  1885  			err := json.Unmarshal(value, &dialParams)
  1886  			if err != nil {
  1887  				return errors.Trace(err)
  1888  			}
  1889  
  1890  			return nil
  1891  		})
  1892  	if err != nil {
  1893  		return nil, errors.Trace(err)
  1894  	}
  1895  
  1896  	return dialParams, nil
  1897  }
  1898  
  1899  // DeleteDialParameters clears any dial parameters associated with the
  1900  // specified server/network ID.
  1901  func DeleteDialParameters(serverIPAddress, networkID string) error {
  1902  
  1903  	key := makeDialParametersKey([]byte(serverIPAddress), []byte(networkID))
  1904  
  1905  	return deleteBucketValue(datastoreDialParametersBucket, key)
  1906  }
  1907  
  1908  // TacticsStorer implements tactics.Storer.
  1909  //
  1910  // Each TacticsStorer datastore operation is wrapped with
  1911  // OpenDataStoreWithoutRetry/CloseDataStore, which enables a limited degree of
  1912  // multiprocess datastore synchronization:
  1913  //
  1914  // One process runs a Controller. Another process runs a stand-alone operation
  1915  // which accesses tactics via GetTactics. For example, SendFeedback.
  1916  //
  1917  // When the Controller is running, it holds an exclusive lock on the datastore
  1918  // and TacticsStorer operations in GetTactics in another process will fail.
  1919  // The stand-alone operation should proceed without tactics. In many cases,
  1920  // this is acceptable since any stand-alone operation network traffic will be
  1921  // tunneled.
  1922  //
  1923  // When the Controller is not running, the TacticsStorer operations in
  1924  // GetTactics in another process will succeed, with no operation holding a
  1925  // datastore lock for longer than the handful of milliseconds required to
  1926  // perform a single datastore operation.
  1927  //
  1928  // If the Controller is started while the stand-alone operation is in
  1929  // progress, the Controller start will not be blocked for long by the brief
  1930  // TacticsStorer datastore locks; the bolt Open call, in particular, has a 1
  1931  // second lock aquisition timeout and OpenDataStore will retry when the
  1932  // datastore file is locked.
  1933  //
  1934  // In this scheme, no attempt is made to detect interleaving datastore writes;
  1935  // that is, if a different process writes tactics in between GetTactics calls
  1936  // to GetTacticsRecord and then SetTacticsRecord. This is because all tactics
  1937  // writes are considered fresh and valid.
  1938  //
  1939  // Using OpenDataStoreWithoutRetry ensures that the GetTactics attempt in the
  1940  // non-Controller operation will quickly fail if the datastore is locked.
  1941  type TacticsStorer struct {
  1942  	config *Config
  1943  }
  1944  
  1945  func (t *TacticsStorer) SetTacticsRecord(networkID string, record []byte) error {
  1946  	err := OpenDataStoreWithoutRetry(t.config)
  1947  	if err != nil {
  1948  		return errors.Trace(err)
  1949  	}
  1950  	defer CloseDataStore()
  1951  	err = setBucketValue(datastoreTacticsBucket, []byte(networkID), record)
  1952  	if err != nil {
  1953  		return errors.Trace(err)
  1954  	}
  1955  	return nil
  1956  }
  1957  
  1958  func (t *TacticsStorer) GetTacticsRecord(networkID string) ([]byte, error) {
  1959  	err := OpenDataStoreWithoutRetry(t.config)
  1960  	if err != nil {
  1961  		return nil, errors.Trace(err)
  1962  	}
  1963  	defer CloseDataStore()
  1964  	value, err := copyBucketValue(datastoreTacticsBucket, []byte(networkID))
  1965  	if err != nil {
  1966  		return nil, errors.Trace(err)
  1967  	}
  1968  	return value, nil
  1969  }
  1970  
  1971  func (t *TacticsStorer) SetSpeedTestSamplesRecord(networkID string, record []byte) error {
  1972  	err := OpenDataStoreWithoutRetry(t.config)
  1973  	if err != nil {
  1974  		return errors.Trace(err)
  1975  	}
  1976  	defer CloseDataStore()
  1977  	err = setBucketValue(datastoreSpeedTestSamplesBucket, []byte(networkID), record)
  1978  	if err != nil {
  1979  		return errors.Trace(err)
  1980  	}
  1981  	return nil
  1982  }
  1983  
  1984  func (t *TacticsStorer) GetSpeedTestSamplesRecord(networkID string) ([]byte, error) {
  1985  	err := OpenDataStoreWithoutRetry(t.config)
  1986  	if err != nil {
  1987  		return nil, errors.Trace(err)
  1988  	}
  1989  	defer CloseDataStore()
  1990  	value, err := copyBucketValue(datastoreSpeedTestSamplesBucket, []byte(networkID))
  1991  	if err != nil {
  1992  		return nil, errors.Trace(err)
  1993  	}
  1994  	return value, nil
  1995  }
  1996  
  1997  // GetTacticsStorer creates a TacticsStorer.
  1998  func GetTacticsStorer(config *Config) *TacticsStorer {
  1999  	return &TacticsStorer{config: config}
  2000  }
  2001  
  2002  // GetAffinityServerEntryAndDialParameters fetches the current affinity server
  2003  // entry value and any corresponding dial parameters for the specified network
  2004  // ID. An error is returned when no affinity server is available. The
  2005  // DialParameter output may be nil when a server entry is found but has no
  2006  // dial parameters.
  2007  func GetAffinityServerEntryAndDialParameters(
  2008  	networkID string) (protocol.ServerEntryFields, *DialParameters, error) {
  2009  
  2010  	var serverEntryFields protocol.ServerEntryFields
  2011  	var dialParams *DialParameters
  2012  
  2013  	err := datastoreView(func(tx *datastoreTx) error {
  2014  
  2015  		keyValues := tx.bucket(datastoreKeyValueBucket)
  2016  		serverEntries := tx.bucket(datastoreServerEntriesBucket)
  2017  		dialParameters := tx.bucket(datastoreDialParametersBucket)
  2018  
  2019  		affinityServerEntryID := keyValues.get(datastoreAffinityServerEntryIDKey)
  2020  		if affinityServerEntryID == nil {
  2021  			return errors.TraceNew("no affinity server available")
  2022  		}
  2023  
  2024  		serverEntryRecord := serverEntries.get(affinityServerEntryID)
  2025  		if serverEntryRecord == nil {
  2026  			return errors.TraceNew("affinity server entry not found")
  2027  		}
  2028  
  2029  		err := json.Unmarshal(
  2030  			serverEntryRecord,
  2031  			&serverEntryFields)
  2032  		if err != nil {
  2033  			return errors.Trace(err)
  2034  		}
  2035  
  2036  		dialParamsKey := makeDialParametersKey(
  2037  			[]byte(serverEntryFields.GetIPAddress()),
  2038  			[]byte(networkID))
  2039  
  2040  		dialParamsRecord := dialParameters.get(dialParamsKey)
  2041  		if dialParamsRecord != nil {
  2042  			err := json.Unmarshal(dialParamsRecord, &dialParams)
  2043  			if err != nil {
  2044  				return errors.Trace(err)
  2045  			}
  2046  		}
  2047  
  2048  		return nil
  2049  	})
  2050  	if err != nil {
  2051  		return nil, nil, errors.Trace(err)
  2052  	}
  2053  
  2054  	return serverEntryFields, dialParams, nil
  2055  }
  2056  
  2057  func setBucketValue(bucket, key, value []byte) error {
  2058  
  2059  	err := datastoreUpdate(func(tx *datastoreTx) error {
  2060  		bucket := tx.bucket(bucket)
  2061  		err := bucket.put(key, value)
  2062  		if err != nil {
  2063  			return errors.Trace(err)
  2064  		}
  2065  		return nil
  2066  	})
  2067  
  2068  	if err != nil {
  2069  		return errors.Trace(err)
  2070  	}
  2071  
  2072  	return nil
  2073  }
  2074  
  2075  func getBucketValue(bucket, key []byte, valueCallback func([]byte) error) error {
  2076  
  2077  	err := datastoreView(func(tx *datastoreTx) error {
  2078  		bucket := tx.bucket(bucket)
  2079  		value := bucket.get(key)
  2080  		return valueCallback(value)
  2081  	})
  2082  
  2083  	if err != nil {
  2084  		return errors.Trace(err)
  2085  	}
  2086  
  2087  	return nil
  2088  }
  2089  
  2090  func deleteBucketValue(bucket, key []byte) error {
  2091  
  2092  	err := datastoreUpdate(func(tx *datastoreTx) error {
  2093  		bucket := tx.bucket(bucket)
  2094  		return bucket.delete(key)
  2095  	})
  2096  
  2097  	if err != nil {
  2098  		return errors.Trace(err)
  2099  	}
  2100  
  2101  	return nil
  2102  }
  2103  
  2104  func copyBucketValue(bucket, key []byte) ([]byte, error) {
  2105  	var valueCopy []byte
  2106  	err := getBucketValue(bucket, key, func(value []byte) error {
  2107  		if value != nil {
  2108  			// Must make a copy as slice is only valid within transaction.
  2109  			valueCopy = make([]byte, len(value))
  2110  			copy(valueCopy, value)
  2111  		}
  2112  		return nil
  2113  	})
  2114  	return valueCopy, err
  2115  }