|
|
@@ -20,12 +20,13 @@
|
|
|
package protocol
|
|
|
|
|
|
import (
|
|
|
+ "bufio"
|
|
|
"bytes"
|
|
|
"encoding/hex"
|
|
|
"encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
- "net"
|
|
|
+ "io"
|
|
|
"strings"
|
|
|
|
|
|
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
|
|
|
@@ -187,25 +188,10 @@ func DecodeServerEntry(
|
|
|
return serverEntry, nil
|
|
|
}
|
|
|
|
|
|
-// ValidateServerEntry checks for malformed server entries.
|
|
|
-// Currently, it checks for a valid ipAddress. This is important since
|
|
|
-// handshake requests submit back to the server a list of known server
|
|
|
-// IP addresses and the handshake API expects well-formed inputs.
|
|
|
-// TODO: validate more fields
|
|
|
-func ValidateServerEntry(serverEntry *ServerEntry) error {
|
|
|
- ipAddr := net.ParseIP(serverEntry.IpAddress)
|
|
|
- if ipAddr == nil {
|
|
|
- errMsg := fmt.Sprintf("server entry has invalid IpAddress: '%s'", serverEntry.IpAddress)
|
|
|
- return common.ContextError(errors.New(errMsg))
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// DecodeAndValidateServerEntryList extracts server entries from the list encoding
|
|
|
+// DecodeServerEntryList extracts server entries from the list encoding
|
|
|
// used by remote server lists and Psiphon server handshake requests.
|
|
|
-// Each server entry is validated and invalid entries are skipped.
|
|
|
// See DecodeServerEntry for note on serverEntrySource/timestamp.
|
|
|
-func DecodeAndValidateServerEntryList(
|
|
|
+func DecodeServerEntryList(
|
|
|
encodedServerEntryList, timestamp,
|
|
|
serverEntrySource string) (serverEntries []*ServerEntry, err error) {
|
|
|
|
|
|
@@ -221,13 +207,58 @@ func DecodeAndValidateServerEntryList(
|
|
|
return nil, common.ContextError(err)
|
|
|
}
|
|
|
|
|
|
- if ValidateServerEntry(serverEntry) != nil {
|
|
|
- // Skip this entry and continue with the next one
|
|
|
- // TODO: invoke a logging callback
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
serverEntries = append(serverEntries, serverEntry)
|
|
|
}
|
|
|
return serverEntries, nil
|
|
|
}
|
|
|
+
|
|
|
+// StreamingServerEntryDecoder performs the DecodeServerEntryList
|
|
|
+// operation, loading only one server entry into memory at a time.
|
|
|
+type StreamingServerEntryDecoder struct {
|
|
|
+ scanner *bufio.Scanner
|
|
|
+ timestamp string
|
|
|
+ serverEntrySource string
|
|
|
+}
|
|
|
+
|
|
|
+// NewStreamingServerEntryDecoder creates a new StreamingServerEntryDecoder.
|
|
|
+func NewStreamingServerEntryDecoder(
|
|
|
+ encodedServerEntryListReader io.Reader,
|
|
|
+ timestamp, serverEntrySource string) *StreamingServerEntryDecoder {
|
|
|
+
|
|
|
+ return &StreamingServerEntryDecoder{
|
|
|
+ scanner: bufio.NewScanner(encodedServerEntryListReader),
|
|
|
+ timestamp: timestamp,
|
|
|
+ serverEntrySource: serverEntrySource,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Next reads and decodes the next server entry from the input stream,
|
|
|
+// returning a nil server entry when the stream is complete.
|
|
|
+//
|
|
|
+// Limitations:
|
|
|
+// - Each encoded server entry line cannot exceed bufio.MaxScanTokenSize,
|
|
|
+// the default buffer size which this decoder uses. This is 64K.
|
|
|
+// - DecodeServerEntry is called on each encoded server entry line, which
|
|
|
+// will allocate memory to hex decode and JSON deserialze the server
|
|
|
+// entry. As this is not presently reusing a fixed buffer, each call
|
|
|
+// will allocate additional memory; garbage collection is necessary to
|
|
|
+// reclaim that memory for reuse for the next server entry. Memory-
|
|
|
+// constrained users could call runtime.GC() after each call to Next.
|
|
|
+//
|
|
|
+func (decoder *StreamingServerEntryDecoder) Next() (*ServerEntry, error) {
|
|
|
+
|
|
|
+ if !decoder.scanner.Scan() {
|
|
|
+ return nil, common.ContextError(decoder.scanner.Err())
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: use scanner.Bytes which doesn't allocate, instead of scanner.Text
|
|
|
+
|
|
|
+ // TODO: skip this entry and continue if can't decode?
|
|
|
+ serverEntry, err := DecodeServerEntry(
|
|
|
+ decoder.scanner.Text(), decoder.timestamp, decoder.serverEntrySource)
|
|
|
+ if err != nil {
|
|
|
+ return nil, common.ContextError(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return serverEntry, nil
|
|
|
+}
|