Update github.com/google/safebrowsing and block on database health (#2837)

Update github.com/google/safebrowsing and block on database health before starting VA 
before starting `boulder-va`.

```
$ go test .
ok  	github.com/google/safebrowsing	4.510s

$ go test .
ok  	github.com/golang/protobuf/ptypes	0.002s
```

Fixes #2742.
This commit is contained in:
Roland Bracewell Shoemaker 2017-06-28 06:44:58 -07:00 committed by Daniel McCarney
parent 71f8ae0e87
commit f5bc9e892a
12 changed files with 793 additions and 216 deletions

8
Godeps/Godeps.json generated
View File

@ -114,6 +114,10 @@
"ImportPath": "github.com/golang/protobuf/proto", "ImportPath": "github.com/golang/protobuf/proto",
"Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" "Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317"
}, },
{
"ImportPath": "github.com/golang/protobuf/ptypes/duration",
"Rev": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317"
},
{ {
"ImportPath": "github.com/google/certificate-transparency-go", "ImportPath": "github.com/google/certificate-transparency-go",
"Rev": "0dac42a6ed448ba220ee315abfaa6d26fd5fc9bb" "Rev": "0dac42a6ed448ba220ee315abfaa6d26fd5fc9bb"
@ -144,11 +148,11 @@
}, },
{ {
"ImportPath": "github.com/google/safebrowsing", "ImportPath": "github.com/google/safebrowsing",
"Rev": "fc74adc270b82ff5a2f288fa84e40213eae713c5" "Rev": "a8c029efb52bae66853e05241150ab338e98fbc7"
}, },
{ {
"ImportPath": "github.com/google/safebrowsing/internal/safebrowsing_proto", "ImportPath": "github.com/google/safebrowsing/internal/safebrowsing_proto",
"Rev": "fc74adc270b82ff5a2f288fa84e40213eae713c5" "Rev": "a8c029efb52bae66853e05241150ab338e98fbc7"
}, },
{ {
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",

View File

@ -3,9 +3,11 @@
package main package main
import ( import (
"context"
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
"time"
safebrowsingv4 "github.com/google/safebrowsing" safebrowsingv4 "github.com/google/safebrowsing"
"github.com/letsencrypt/boulder/cmd" "github.com/letsencrypt/boulder/cmd"
@ -119,6 +121,12 @@ func newGoogleSafeBrowsingV4(gsb *cmd.GoogleSafeBrowsingConfig, logger blog.Logg
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx, cancel := context.WithTimeout(context.Background(), time.Second*60)
defer cancel() // avoid leak
err = sb.WaitUntilReady(ctx)
if err != nil {
return nil, err
}
return gsbAdapter{sb}, nil return gsbAdapter{sb}, nil
} }

View File

@ -0,0 +1,114 @@
// Code generated by protoc-gen-go.
// source: github.com/golang/protobuf/ptypes/duration/duration.proto
// DO NOT EDIT!
/*
Package duration is a generated protocol buffer package.
It is generated from these files:
github.com/golang/protobuf/ptypes/duration/duration.proto
It has these top-level messages:
Duration
*/
package duration
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
//
// Example 1: Compute Duration from two Timestamps in pseudo code.
//
// Timestamp start = ...;
// Timestamp end = ...;
// Duration duration = ...;
//
// duration.seconds = end.seconds - start.seconds;
// duration.nanos = end.nanos - start.nanos;
//
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (durations.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
//
// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
//
// Timestamp start = ...;
// Duration duration = ...;
// Timestamp end = ...;
//
// end.seconds = start.seconds + duration.seconds;
// end.nanos = start.nanos + duration.nanos;
//
// if (end.nanos < 0) {
// end.seconds -= 1;
// end.nanos += 1000000000;
// } else if (end.nanos >= 1000000000) {
// end.seconds += 1;
// end.nanos -= 1000000000;
// }
//
//
type Duration struct {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive.
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
}
func (m *Duration) Reset() { *m = Duration{} }
func (m *Duration) String() string { return proto.CompactTextString(m) }
func (*Duration) ProtoMessage() {}
func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (*Duration) XXX_WellKnownType() string { return "Duration" }
func init() {
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
}
func init() {
proto.RegisterFile("github.com/golang/protobuf/ptypes/duration/duration.proto", fileDescriptor0)
}
var fileDescriptor0 = []byte{
// 189 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0x29,
0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0x83, 0x33, 0xf4, 0xc0, 0x2a, 0x84, 0xf8, 0xd3, 0xf3, 0xf3,
0xd3, 0x73, 0x52, 0xf5, 0x60, 0xea, 0x95, 0xac, 0xb8, 0x38, 0x5c, 0xa0, 0x4a, 0x84, 0x24, 0xb8,
0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83, 0x60,
0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, 0xd6,
0x20, 0x08, 0xc7, 0xa9, 0x86, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x48, 0x27, 0x5e, 0x98,
0x81, 0x01, 0x20, 0x91, 0x00, 0xc6, 0x28, 0x2d, 0xe2, 0xdd, 0xbb, 0x80, 0x91, 0x71, 0x11, 0x13,
0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xb9, 0x01, 0x50, 0xa5, 0x7a, 0xe1, 0xa9,
0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x2d, 0x49, 0x6c, 0x60, 0x33, 0x8c, 0x01,
0x01, 0x00, 0x00, 0xff, 0xff, 0x62, 0xfb, 0xb1, 0x51, 0x0e, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,98 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "github.com/golang/protobuf/ptypes/duration";
option java_package = "com.google.protobuf";
option java_outer_classname = "DurationProto";
option java_multiple_files = true;
option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
//
// Example 1: Compute Duration from two Timestamps in pseudo code.
//
// Timestamp start = ...;
// Timestamp end = ...;
// Duration duration = ...;
//
// duration.seconds = end.seconds - start.seconds;
// duration.nanos = end.nanos - start.nanos;
//
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (durations.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
//
// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
//
// Timestamp start = ...;
// Duration duration = ...;
// Timestamp end = ...;
//
// end.seconds = start.seconds + duration.seconds;
// end.nanos = start.nanos + duration.nanos;
//
// if (end.nanos < 0) {
// end.seconds -= 1;
// end.nanos += 1000000000;
// } else if (end.nanos >= 1000000000) {
// end.seconds += 1;
// end.nanos -= 1000000000;
// }
//
//
message Duration {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive.
int64 seconds = 1;
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
int32 nanos = 2;
}

View File

@ -1,10 +1,10 @@
language: go language: go
go: go:
- 1.6
- 1.7 - 1.7
- 1.8
script: script:
- test -z "$(gofmt -s -l $(find . -name '*.go' -type f -print) | tee /dev/stderr)" - test -z "$(gofmt -s -l $(find . -name '*.go' -type f -print) | tee /dev/stderr)"
- go test -v ./... - go test -v $(go list ./... | grep -v vendor)
- go test -race -v ./... - go test -race -v $(go list ./... | grep -v vendor)

View File

@ -21,6 +21,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"time"
pb "github.com/google/safebrowsing/internal/safebrowsing_proto" pb "github.com/google/safebrowsing/internal/safebrowsing_proto"
@ -40,14 +41,14 @@ type api interface {
// netAPI is an api object that talks to the server over HTTP. // netAPI is an api object that talks to the server over HTTP.
type netAPI struct { type netAPI struct {
client http.Client client *http.Client
url *url.URL url *url.URL
} }
// newNetAPI creates a new netAPI object pointed at the provided root URL. // newNetAPI creates a new netAPI object pointed at the provided root URL.
// For every request, it will use the provided API key. // For every request, it will use the provided API key.
// If the protocol is not specified in root, then this defaults to using HTTPS. // If the protocol is not specified in root, then this defaults to using HTTPS.
func newNetAPI(root string, key string) (*netAPI, error) { func newNetAPI(root string, key string, timeout time.Duration) (*netAPI, error) {
if !strings.Contains(root, "://") { if !strings.Contains(root, "://") {
root = "https://" + root root = "https://" + root
} }
@ -60,7 +61,7 @@ func newNetAPI(root string, key string) (*netAPI, error) {
q.Set("key", key) q.Set("key", key)
q.Set("alt", "proto") q.Set("alt", "proto")
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
return &netAPI{url: u}, nil return &netAPI{url: u, client: &http.Client{Timeout: timeout}}, nil
} }
// doRequests performs a POST to requestPath. It uses the marshaled form of req // doRequests performs a POST to requestPath. It uses the marshaled form of req

View File

@ -80,7 +80,12 @@ func (c *cache) Update(req *pb.FindFullHashesRequest, resp *pb.FindFullHashesRes
if c.pttls[fullHash] == nil { if c.pttls[fullHash] == nil {
c.pttls[fullHash] = make(map[ThreatDescriptor]time.Time) c.pttls[fullHash] = make(map[ThreatDescriptor]time.Time)
} }
dur := time.Duration(tm.GetCacheDuration().Seconds) * time.Second var dur time.Duration
if tmCacheDur := tm.GetCacheDuration(); tmCacheDur != nil {
dur = time.Duration(tm.GetCacheDuration().Seconds) * time.Second
} else {
dur = 0
}
td := ThreatDescriptor{ td := ThreatDescriptor{
ThreatType: ThreatType(tm.ThreatType), ThreatType: ThreatType(tm.ThreatType),
PlatformType: PlatformType(tm.PlatformType), PlatformType: PlatformType(tm.PlatformType),

View File

@ -20,6 +20,7 @@ import (
"encoding/gob" "encoding/gob"
"errors" "errors"
"log" "log"
"math/rand"
"os" "os"
"sync" "sync"
"time" "time"
@ -30,7 +31,11 @@ import (
// jitter is the maximum amount of time that we expect an API list update to // jitter is the maximum amount of time that we expect an API list update to
// actually take. We add this time to the update period time to give some // actually take. We add this time to the update period time to give some
// leeway before declaring the database as stale. // leeway before declaring the database as stale.
const jitter = 30 * time.Second const (
maxRetryDelay = 24 * time.Hour
baseRetryDelay = 15 * time.Minute
jitter = 30 * time.Second
)
// database tracks the state of the threat lists published by the Safe Browsing // database tracks the state of the threat lists published by the Safe Browsing
// API. Since the global blacklist is constantly changing, the contents of the // API. Since the global blacklist is constantly changing, the contents of the
@ -63,8 +68,10 @@ type database struct {
tfl threatsForLookup tfl threatsForLookup
ml sync.RWMutex // Protects tfl, err, and last ml sync.RWMutex // Protects tfl, err, and last
err error // Last error encountered err error // Last error encountered
last time.Time // Last time the threat list were synced readyCh chan struct{} // Used for waiting until not in an error state.
last time.Time // Last time the threat list were synced
updateAPIErrors uint // Number of times we attempted to contact the api and failed
log *log.Logger log *log.Logger
} }
@ -94,11 +101,14 @@ type databaseFormat struct {
// Init initializes the database from the specified file in config.DBPath. // Init initializes the database from the specified file in config.DBPath.
// It reports true if the database was successfully loaded. // It reports true if the database was successfully loaded.
func (db *database) Init(config *Config, logger *log.Logger) bool { func (db *database) Init(config *Config, logger *log.Logger) bool {
db.mu.Lock()
defer db.mu.Unlock()
db.setError(errors.New("not intialized"))
db.config = config db.config = config
db.log = logger db.log = logger
if db.config.DBPath == "" { if db.config.DBPath == "" {
db.log.Printf("no database file specified") db.log.Printf("no database file specified")
db.setError(errStale) db.setError(errors.New("no database loaded"))
return false return false
} }
dbf, err := loadDatabase(db.config.DBPath) dbf, err := loadDatabase(db.config.DBPath)
@ -112,7 +122,9 @@ func (db *database) Init(config *Config, logger *log.Logger) bool {
// superset of the specified configuration. // superset of the specified configuration.
if db.config.now().Sub(dbf.Time) > (db.config.UpdatePeriod + jitter) { if db.config.now().Sub(dbf.Time) > (db.config.UpdatePeriod + jitter) {
db.log.Printf("database loaded is stale") db.log.Printf("database loaded is stale")
db.setError(errStale) db.ml.Lock()
defer db.ml.Unlock()
db.setStale()
return false return false
} }
tfuNew := make(threatsForUpdate) tfuNew := make(threatsForUpdate)
@ -120,8 +132,8 @@ func (db *database) Init(config *Config, logger *log.Logger) bool {
if row, ok := dbf.Table[td]; ok { if row, ok := dbf.Table[td]; ok {
tfuNew[td] = row tfuNew[td] = row
} else { } else {
db.log.Printf("database configuration mismatch") db.log.Printf("database configuration mismatch, missing %v", td)
db.setError(errStale) db.setError(errors.New("database configuration mismatch"))
return false return false
} }
} }
@ -140,15 +152,39 @@ func (db *database) Status() error {
return db.err return db.err
} }
if db.config.now().Sub(db.last) > (db.config.UpdatePeriod + jitter) { if db.config.now().Sub(db.last) > (db.config.UpdatePeriod + jitter) {
return errStale db.setStale()
return db.err
} }
return nil return nil
} }
// UpdateLag reports the amount of time in between when we expected to run
// a database update and the current time
func (db *database) UpdateLag() time.Duration {
lag := db.SinceLastUpdate()
if lag < db.config.UpdatePeriod {
return 0
}
return lag - db.config.UpdatePeriod
}
// SinceLastUpdate gives the duration since the last database update
func (db *database) SinceLastUpdate() time.Duration {
db.ml.RLock()
defer db.ml.RUnlock()
return db.config.now().Sub(db.last)
}
// Ready returns a channel that's closed when the database is ready for queries.
func (db *database) Ready() <-chan struct{} {
return db.readyCh
}
// Update synchronizes the local threat lists with those maintained by the // Update synchronizes the local threat lists with those maintained by the
// global Safe Browsing API servers. If the update is successful, Status should // global Safe Browsing API servers. If the update is successful, Status should
// report a nil error. // report a nil error.
func (db *database) Update(api api) { func (db *database) Update(api api) (time.Duration, bool) {
db.mu.Lock() db.mu.Lock()
defer db.mu.Unlock() defer db.mu.Unlock()
@ -183,23 +219,42 @@ func (db *database) Update(api api) {
last := db.config.now() last := db.config.now()
resp, err := api.ListUpdate(req) resp, err := api.ListUpdate(req)
if err != nil { if err != nil {
db.log.Printf("ListUpdate failure: %v", err) db.log.Printf("ListUpdate failure (%d): %v", db.updateAPIErrors+1, err)
db.setError(err) db.setError(err)
return // backoff strategy: MIN((2**N-1 * 15 minutes) * (RAND + 1), 24 hours)
n := 1 << db.updateAPIErrors
delay := time.Duration(float64(n) * (rand.Float64() + 1) * float64(baseRetryDelay))
if delay > maxRetryDelay {
delay = maxRetryDelay
}
db.updateAPIErrors++
return delay, false
}
db.updateAPIErrors = 0
// add jitter to wait time to avoid all servers lining up
nextUpdateWait := db.config.UpdatePeriod + time.Duration(rand.Int31n(60)-30)*time.Second
if resp.MinimumWaitDuration != nil {
serverMinWait := time.Duration(resp.MinimumWaitDuration.Seconds)*time.Second + time.Duration(resp.MinimumWaitDuration.Nanos)
if serverMinWait > nextUpdateWait {
nextUpdateWait = serverMinWait
db.log.Printf("Server requested next update in %v", nextUpdateWait)
}
} }
if len(resp.ListUpdateResponses) != numTypes { if len(resp.ListUpdateResponses) != numTypes {
db.setError(errors.New("safebrowsing: threat list count mismatch"))
db.log.Printf("invalid server response: got %d, want %d threat lists", db.log.Printf("invalid server response: got %d, want %d threat lists",
len(resp.ListUpdateResponses), numTypes) len(resp.ListUpdateResponses), numTypes)
db.setError(errors.New("safebrowsing: threat list count mismatch")) return nextUpdateWait, false
return
} }
// Update the threat database with the response. // Update the threat database with the response.
db.generateThreatsForUpdate() db.generateThreatsForUpdate()
if err := db.tfu.update(resp); err != nil { if err := db.tfu.update(resp); err != nil {
db.log.Printf("update failure: %v", err)
db.setError(err) db.setError(err)
return db.log.Printf("update failure: %v", err)
db.tfu = nil
return nextUpdateWait, false
} }
dbf := databaseFormat{make(threatsForUpdate), last} dbf := databaseFormat{make(threatsForUpdate), last}
for td, phs := range db.tfu { for td, phs := range db.tfu {
@ -215,6 +270,8 @@ func (db *database) Update(api api) {
db.log.Printf("save failure: %v", err) db.log.Printf("save failure: %v", err)
} }
} }
return nextUpdateWait, true
} }
// Lookup looks up the full hash in the threat list and returns a partial // Lookup looks up the full hash in the threat list and returns a partial
@ -242,10 +299,38 @@ func (db *database) setError(err error) {
db.tfu = nil db.tfu = nil
db.ml.Lock() db.ml.Lock()
if db.err == nil {
db.readyCh = make(chan struct{})
}
db.tfl, db.err, db.last = nil, err, time.Time{} db.tfl, db.err, db.last = nil, err, time.Time{}
db.ml.Unlock() db.ml.Unlock()
} }
// setStale sets the error state to a stale message, without clearing
// the database state.
//
// This assumes that the db.ml lock is already held.
func (db *database) setStale() {
if db.err == nil {
db.readyCh = make(chan struct{})
}
db.err = errStale
}
// clearError clears the db error state, and unblocks any callers of
// WaitUntilReady.
//
// This assumes that the db.mu lock is already held.
func (db *database) clearError() {
db.ml.Lock()
defer db.ml.Unlock()
if db.err != nil {
close(db.readyCh)
}
db.err = nil
}
// generateThreatsForUpdate regenerates the threatsForUpdate hashes from // generateThreatsForUpdate regenerates the threatsForUpdate hashes from
// the threatsForLookup. We do this to avoid holding onto the hash lists for // the threatsForLookup. We do this to avoid holding onto the hash lists for
// a long time, needlessly occupying lots of memory. // a long time, needlessly occupying lots of memory.
@ -284,10 +369,11 @@ func (db *database) generateThreatsForLookups(last time.Time) {
db.ml.Lock() db.ml.Lock()
wasBad := db.err != nil wasBad := db.err != nil
db.tfl, db.err, db.last = tfl, nil, last db.tfl, db.last = tfl, last
db.ml.Unlock() db.ml.Unlock()
if wasBad { if wasBad {
db.clearError()
db.log.Printf("database is now healthy") db.log.Printf("database is now healthy")
} }
} }
@ -424,7 +510,9 @@ func (tfu threatsForUpdate) update(resp *pb.FetchThreatListUpdatesResponse) erro
return err return err
} }
phs.SHA256 = m.GetChecksum().Sha256 if cs := m.GetChecksum(); cs != nil {
phs.SHA256 = cs.Sha256
}
if !bytes.Equal(phs.SHA256, phs.Hashes.SHA256()) { if !bytes.Equal(phs.SHA256, phs.Hashes.SHA256()) {
return errors.New("safebrowsing: threat list SHA256 mismatch") return errors.New("safebrowsing: threat list SHA256 mismatch")
} }

View File

@ -27,13 +27,13 @@ It has these top-level messages:
ThreatEntryMetadata ThreatEntryMetadata
ThreatListDescriptor ThreatListDescriptor
ListThreatListsResponse ListThreatListsResponse
Duration
*/ */
package safebrowsing_proto package safebrowsing_proto
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
import fmt "fmt" import fmt "fmt"
import math "math" import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/duration"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -42,7 +42,9 @@ var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
const _ = proto.ProtoPackageIsVersion1 // A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Types of threats. // Types of threats.
type ThreatType int32 type ThreatType int32
@ -229,11 +231,11 @@ func (FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType) EnumDescri
// checking for matches in threat lists. // checking for matches in threat lists.
type ThreatInfo struct { type ThreatInfo struct {
// The threat types to be checked. // The threat types to be checked.
ThreatTypes []ThreatType `protobuf:"varint,1,rep,name=threat_types,json=threatTypes,enum=safebrowsing_proto.ThreatType" json:"threat_types,omitempty"` ThreatTypes []ThreatType `protobuf:"varint,1,rep,packed,name=threat_types,json=threatTypes,enum=safebrowsing_proto.ThreatType" json:"threat_types,omitempty"`
// The platform types to be checked. // The platform types to be checked.
PlatformTypes []PlatformType `protobuf:"varint,2,rep,name=platform_types,json=platformTypes,enum=safebrowsing_proto.PlatformType" json:"platform_types,omitempty"` PlatformTypes []PlatformType `protobuf:"varint,2,rep,packed,name=platform_types,json=platformTypes,enum=safebrowsing_proto.PlatformType" json:"platform_types,omitempty"`
// The entry types to be checked. // The entry types to be checked.
ThreatEntryTypes []ThreatEntryType `protobuf:"varint,4,rep,name=threat_entry_types,json=threatEntryTypes,enum=safebrowsing_proto.ThreatEntryType" json:"threat_entry_types,omitempty"` ThreatEntryTypes []ThreatEntryType `protobuf:"varint,4,rep,packed,name=threat_entry_types,json=threatEntryTypes,enum=safebrowsing_proto.ThreatEntryType" json:"threat_entry_types,omitempty"`
// The threat entries to be checked. // The threat entries to be checked.
ThreatEntries []*ThreatEntry `protobuf:"bytes,3,rep,name=threat_entries,json=threatEntries" json:"threat_entries,omitempty"` ThreatEntries []*ThreatEntry `protobuf:"bytes,3,rep,name=threat_entries,json=threatEntries" json:"threat_entries,omitempty"`
} }
@ -243,6 +245,27 @@ func (m *ThreatInfo) String() string { return proto.CompactTextString
func (*ThreatInfo) ProtoMessage() {} func (*ThreatInfo) ProtoMessage() {}
func (*ThreatInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*ThreatInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ThreatInfo) GetThreatTypes() []ThreatType {
if m != nil {
return m.ThreatTypes
}
return nil
}
func (m *ThreatInfo) GetPlatformTypes() []PlatformType {
if m != nil {
return m.PlatformTypes
}
return nil
}
func (m *ThreatInfo) GetThreatEntryTypes() []ThreatEntryType {
if m != nil {
return m.ThreatEntryTypes
}
return nil
}
func (m *ThreatInfo) GetThreatEntries() []*ThreatEntry { func (m *ThreatInfo) GetThreatEntries() []*ThreatEntry {
if m != nil { if m != nil {
return m.ThreatEntries return m.ThreatEntries
@ -264,7 +287,7 @@ type ThreatMatch struct {
ThreatEntryMetadata *ThreatEntryMetadata `protobuf:"bytes,4,opt,name=threat_entry_metadata,json=threatEntryMetadata" json:"threat_entry_metadata,omitempty"` ThreatEntryMetadata *ThreatEntryMetadata `protobuf:"bytes,4,opt,name=threat_entry_metadata,json=threatEntryMetadata" json:"threat_entry_metadata,omitempty"`
// The cache lifetime for the returned match. Clients must not cache this // The cache lifetime for the returned match. Clients must not cache this
// response for more than this duration to avoid false positives. // response for more than this duration to avoid false positives.
CacheDuration *Duration `protobuf:"bytes,5,opt,name=cache_duration,json=cacheDuration" json:"cache_duration,omitempty"` CacheDuration *google_protobuf.Duration `protobuf:"bytes,5,opt,name=cache_duration,json=cacheDuration" json:"cache_duration,omitempty"`
} }
func (m *ThreatMatch) Reset() { *m = ThreatMatch{} } func (m *ThreatMatch) Reset() { *m = ThreatMatch{} }
@ -272,6 +295,27 @@ func (m *ThreatMatch) String() string { return proto.CompactTextStrin
func (*ThreatMatch) ProtoMessage() {} func (*ThreatMatch) ProtoMessage() {}
func (*ThreatMatch) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } func (*ThreatMatch) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *ThreatMatch) GetThreatType() ThreatType {
if m != nil {
return m.ThreatType
}
return ThreatType_THREAT_TYPE_UNSPECIFIED
}
func (m *ThreatMatch) GetPlatformType() PlatformType {
if m != nil {
return m.PlatformType
}
return PlatformType_PLATFORM_TYPE_UNSPECIFIED
}
func (m *ThreatMatch) GetThreatEntryType() ThreatEntryType {
if m != nil {
return m.ThreatEntryType
}
return ThreatEntryType_THREAT_ENTRY_TYPE_UNSPECIFIED
}
func (m *ThreatMatch) GetThreat() *ThreatEntry { func (m *ThreatMatch) GetThreat() *ThreatEntry {
if m != nil { if m != nil {
return m.Threat return m.Threat
@ -286,7 +330,7 @@ func (m *ThreatMatch) GetThreatEntryMetadata() *ThreatEntryMetadata {
return nil return nil
} }
func (m *ThreatMatch) GetCacheDuration() *Duration { func (m *ThreatMatch) GetCacheDuration() *google_protobuf.Duration {
if m != nil { if m != nil {
return m.CacheDuration return m.CacheDuration
} }
@ -394,6 +438,34 @@ func (*FetchThreatListUpdatesRequest_ListUpdateRequest) Descriptor() ([]byte, []
return fileDescriptor0, []int{4, 0} return fileDescriptor0, []int{4, 0}
} }
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetThreatType() ThreatType {
if m != nil {
return m.ThreatType
}
return ThreatType_THREAT_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetPlatformType() PlatformType {
if m != nil {
return m.PlatformType
}
return PlatformType_PLATFORM_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetThreatEntryType() ThreatEntryType {
if m != nil {
return m.ThreatEntryType
}
return ThreatEntryType_THREAT_ENTRY_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetState() []byte {
if m != nil {
return m.State
}
return nil
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetConstraints() *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints { func (m *FetchThreatListUpdatesRequest_ListUpdateRequest) GetConstraints() *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints {
if m != nil { if m != nil {
return m.Constraints return m.Constraints
@ -416,7 +488,7 @@ type FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints struct {
// 3166-1 alpha-2 format. // 3166-1 alpha-2 format.
Region string `protobuf:"bytes,3,opt,name=region" json:"region,omitempty"` Region string `protobuf:"bytes,3,opt,name=region" json:"region,omitempty"`
// The compression types supported by the client. // The compression types supported by the client.
SupportedCompressions []CompressionType `protobuf:"varint,4,rep,name=supported_compressions,json=supportedCompressions,enum=safebrowsing_proto.CompressionType" json:"supported_compressions,omitempty"` SupportedCompressions []CompressionType `protobuf:"varint,4,rep,packed,name=supported_compressions,json=supportedCompressions,enum=safebrowsing_proto.CompressionType" json:"supported_compressions,omitempty"`
} }
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) Reset() { func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) Reset() {
@ -430,13 +502,41 @@ func (*FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) Descriptor()
return fileDescriptor0, []int{4, 0, 0} return fileDescriptor0, []int{4, 0, 0}
} }
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) GetMaxUpdateEntries() int32 {
if m != nil {
return m.MaxUpdateEntries
}
return 0
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) GetMaxDatabaseEntries() int32 {
if m != nil {
return m.MaxDatabaseEntries
}
return 0
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) GetRegion() string {
if m != nil {
return m.Region
}
return ""
}
func (m *FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints) GetSupportedCompressions() []CompressionType {
if m != nil {
return m.SupportedCompressions
}
return nil
}
// Response type for threat list update requests. // Response type for threat list update requests.
type FetchThreatListUpdatesResponse struct { type FetchThreatListUpdatesResponse struct {
// The list updates requested by the clients. // The list updates requested by the clients.
ListUpdateResponses []*FetchThreatListUpdatesResponse_ListUpdateResponse `protobuf:"bytes,1,rep,name=list_update_responses,json=listUpdateResponses" json:"list_update_responses,omitempty"` ListUpdateResponses []*FetchThreatListUpdatesResponse_ListUpdateResponse `protobuf:"bytes,1,rep,name=list_update_responses,json=listUpdateResponses" json:"list_update_responses,omitempty"`
// The minimum duration the client must wait before issuing any update // The minimum duration the client must wait before issuing any update
// request. If this field is not set clients may update as soon as they want. // request. If this field is not set clients may update as soon as they want.
MinimumWaitDuration *Duration `protobuf:"bytes,2,opt,name=minimum_wait_duration,json=minimumWaitDuration" json:"minimum_wait_duration,omitempty"` MinimumWaitDuration *google_protobuf.Duration `protobuf:"bytes,2,opt,name=minimum_wait_duration,json=minimumWaitDuration" json:"minimum_wait_duration,omitempty"`
} }
func (m *FetchThreatListUpdatesResponse) Reset() { *m = FetchThreatListUpdatesResponse{} } func (m *FetchThreatListUpdatesResponse) Reset() { *m = FetchThreatListUpdatesResponse{} }
@ -451,7 +551,7 @@ func (m *FetchThreatListUpdatesResponse) GetListUpdateResponses() []*FetchThreat
return nil return nil
} }
func (m *FetchThreatListUpdatesResponse) GetMinimumWaitDuration() *Duration { func (m *FetchThreatListUpdatesResponse) GetMinimumWaitDuration() *google_protobuf.Duration {
if m != nil { if m != nil {
return m.MinimumWaitDuration return m.MinimumWaitDuration
} }
@ -496,6 +596,34 @@ func (*FetchThreatListUpdatesResponse_ListUpdateResponse) Descriptor() ([]byte,
return fileDescriptor0, []int{5, 0} return fileDescriptor0, []int{5, 0}
} }
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetThreatType() ThreatType {
if m != nil {
return m.ThreatType
}
return ThreatType_THREAT_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetThreatEntryType() ThreatEntryType {
if m != nil {
return m.ThreatEntryType
}
return ThreatEntryType_THREAT_ENTRY_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetPlatformType() PlatformType {
if m != nil {
return m.PlatformType
}
return PlatformType_PLATFORM_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetResponseType() FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType {
if m != nil {
return m.ResponseType
}
return FetchThreatListUpdatesResponse_ListUpdateResponse_RESPONSE_TYPE_UNSPECIFIED
}
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetAdditions() []*ThreatEntrySet { func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetAdditions() []*ThreatEntrySet {
if m != nil { if m != nil {
return m.Additions return m.Additions
@ -510,6 +638,13 @@ func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetRemovals() []*Thr
return nil return nil
} }
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetNewClientState() []byte {
if m != nil {
return m.NewClientState
}
return nil
}
func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetChecksum() *Checksum { func (m *FetchThreatListUpdatesResponse_ListUpdateResponse) GetChecksum() *Checksum {
if m != nil { if m != nil {
return m.Checksum return m.Checksum
@ -539,6 +674,13 @@ func (m *FindFullHashesRequest) GetClient() *ClientInfo {
return nil return nil
} }
func (m *FindFullHashesRequest) GetClientStates() [][]byte {
if m != nil {
return m.ClientStates
}
return nil
}
func (m *FindFullHashesRequest) GetThreatInfo() *ThreatInfo { func (m *FindFullHashesRequest) GetThreatInfo() *ThreatInfo {
if m != nil { if m != nil {
return m.ThreatInfo return m.ThreatInfo
@ -553,10 +695,10 @@ type FindFullHashesResponse struct {
// The minimum duration the client must wait before issuing any find hashes // The minimum duration the client must wait before issuing any find hashes
// request. If this field is not set, clients can issue a request as soon as // request. If this field is not set, clients can issue a request as soon as
// they want. // they want.
MinimumWaitDuration *Duration `protobuf:"bytes,2,opt,name=minimum_wait_duration,json=minimumWaitDuration" json:"minimum_wait_duration,omitempty"` MinimumWaitDuration *google_protobuf.Duration `protobuf:"bytes,2,opt,name=minimum_wait_duration,json=minimumWaitDuration" json:"minimum_wait_duration,omitempty"`
// For requested entities that did not match the threat list, how long to // For requested entities that did not match the threat list, how long to
// cache the response. // cache the response.
NegativeCacheDuration *Duration `protobuf:"bytes,3,opt,name=negative_cache_duration,json=negativeCacheDuration" json:"negative_cache_duration,omitempty"` NegativeCacheDuration *google_protobuf.Duration `protobuf:"bytes,3,opt,name=negative_cache_duration,json=negativeCacheDuration" json:"negative_cache_duration,omitempty"`
} }
func (m *FindFullHashesResponse) Reset() { *m = FindFullHashesResponse{} } func (m *FindFullHashesResponse) Reset() { *m = FindFullHashesResponse{} }
@ -571,14 +713,14 @@ func (m *FindFullHashesResponse) GetMatches() []*ThreatMatch {
return nil return nil
} }
func (m *FindFullHashesResponse) GetMinimumWaitDuration() *Duration { func (m *FindFullHashesResponse) GetMinimumWaitDuration() *google_protobuf.Duration {
if m != nil { if m != nil {
return m.MinimumWaitDuration return m.MinimumWaitDuration
} }
return nil return nil
} }
func (m *FindFullHashesResponse) GetNegativeCacheDuration() *Duration { func (m *FindFullHashesResponse) GetNegativeCacheDuration() *google_protobuf.Duration {
if m != nil { if m != nil {
return m.NegativeCacheDuration return m.NegativeCacheDuration
} }
@ -599,6 +741,20 @@ func (m *ClientInfo) String() string { return proto.CompactTextString
func (*ClientInfo) ProtoMessage() {} func (*ClientInfo) ProtoMessage() {}
func (*ClientInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } func (*ClientInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *ClientInfo) GetClientId() string {
if m != nil {
return m.ClientId
}
return ""
}
func (m *ClientInfo) GetClientVersion() string {
if m != nil {
return m.ClientVersion
}
return ""
}
// The expected state of a client's local database. // The expected state of a client's local database.
type Checksum struct { type Checksum struct {
// The SHA256 hash of the client state; that is, of the sorted list of all // The SHA256 hash of the client state; that is, of the sorted list of all
@ -611,6 +767,13 @@ func (m *Checksum) String() string { return proto.CompactTextString(m
func (*Checksum) ProtoMessage() {} func (*Checksum) ProtoMessage() {}
func (*Checksum) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } func (*Checksum) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
func (m *Checksum) GetSha256() []byte {
if m != nil {
return m.Sha256
}
return nil
}
// An individual threat; for example, a malicious URL or its hash // An individual threat; for example, a malicious URL or its hash
// representation. Only one of these fields should be set. // representation. Only one of these fields should be set.
type ThreatEntry struct { type ThreatEntry struct {
@ -626,6 +789,20 @@ func (m *ThreatEntry) String() string { return proto.CompactTextStrin
func (*ThreatEntry) ProtoMessage() {} func (*ThreatEntry) ProtoMessage() {}
func (*ThreatEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } func (*ThreatEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (m *ThreatEntry) GetHash() []byte {
if m != nil {
return m.Hash
}
return nil
}
func (m *ThreatEntry) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
// A set of threats that should be added or removed from a client's local // A set of threats that should be added or removed from a client's local
// database. // database.
type ThreatEntrySet struct { type ThreatEntrySet struct {
@ -648,6 +825,13 @@ func (m *ThreatEntrySet) String() string { return proto.CompactTextSt
func (*ThreatEntrySet) ProtoMessage() {} func (*ThreatEntrySet) ProtoMessage() {}
func (*ThreatEntrySet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } func (*ThreatEntrySet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (m *ThreatEntrySet) GetCompressionType() CompressionType {
if m != nil {
return m.CompressionType
}
return CompressionType_COMPRESSION_TYPE_UNSPECIFIED
}
func (m *ThreatEntrySet) GetRawHashes() *RawHashes { func (m *ThreatEntrySet) GetRawHashes() *RawHashes {
if m != nil { if m != nil {
return m.RawHashes return m.RawHashes
@ -679,7 +863,7 @@ func (m *ThreatEntrySet) GetRiceIndices() *RiceDeltaEncoding {
// A set of raw indices to remove from a local list. // A set of raw indices to remove from a local list.
type RawIndices struct { type RawIndices struct {
// The indices to remove from a lexicographically-sorted local list. // The indices to remove from a lexicographically-sorted local list.
Indices []int32 `protobuf:"varint,1,rep,name=indices" json:"indices,omitempty"` Indices []int32 `protobuf:"varint,1,rep,packed,name=indices" json:"indices,omitempty"`
} }
func (m *RawIndices) Reset() { *m = RawIndices{} } func (m *RawIndices) Reset() { *m = RawIndices{} }
@ -687,6 +871,13 @@ func (m *RawIndices) String() string { return proto.CompactTextString
func (*RawIndices) ProtoMessage() {} func (*RawIndices) ProtoMessage() {}
func (*RawIndices) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } func (*RawIndices) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *RawIndices) GetIndices() []int32 {
if m != nil {
return m.Indices
}
return nil
}
// The uncompressed threat entries in hash format of a particular prefix length. // The uncompressed threat entries in hash format of a particular prefix length.
// Hashes can be anywhere from 4 to 32 bytes in size. A large majority are 4 // Hashes can be anywhere from 4 to 32 bytes in size. A large majority are 4
// bytes, but some hashes are lengthened if they collide with the hash of a // bytes, but some hashes are lengthened if they collide with the hash of a
@ -708,6 +899,20 @@ func (m *RawHashes) String() string { return proto.CompactTextString(
func (*RawHashes) ProtoMessage() {} func (*RawHashes) ProtoMessage() {}
func (*RawHashes) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } func (*RawHashes) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (m *RawHashes) GetPrefixSize() int32 {
if m != nil {
return m.PrefixSize
}
return 0
}
func (m *RawHashes) GetRawHashes() []byte {
if m != nil {
return m.RawHashes
}
return nil
}
// The Rice-Golomb encoded data. Used for sending compressed 4-byte hashes or // The Rice-Golomb encoded data. Used for sending compressed 4-byte hashes or
// compressed removal indices. // compressed removal indices.
type RiceDeltaEncoding struct { type RiceDeltaEncoding struct {
@ -730,6 +935,34 @@ func (m *RiceDeltaEncoding) String() string { return proto.CompactTex
func (*RiceDeltaEncoding) ProtoMessage() {} func (*RiceDeltaEncoding) ProtoMessage() {}
func (*RiceDeltaEncoding) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } func (*RiceDeltaEncoding) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (m *RiceDeltaEncoding) GetFirstValue() int64 {
if m != nil {
return m.FirstValue
}
return 0
}
func (m *RiceDeltaEncoding) GetRiceParameter() int32 {
if m != nil {
return m.RiceParameter
}
return 0
}
func (m *RiceDeltaEncoding) GetNumEntries() int32 {
if m != nil {
return m.NumEntries
}
return 0
}
func (m *RiceDeltaEncoding) GetEncodedData() []byte {
if m != nil {
return m.EncodedData
}
return nil
}
// The metadata associated with a specific threat entry. The client is expected // The metadata associated with a specific threat entry. The client is expected
// to know the metadata key/value pairs associated with each threat type. // to know the metadata key/value pairs associated with each threat type.
type ThreatEntryMetadata struct { type ThreatEntryMetadata struct {
@ -764,6 +997,20 @@ func (*ThreatEntryMetadata_MetadataEntry) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{15, 0} return fileDescriptor0, []int{15, 0}
} }
func (m *ThreatEntryMetadata_MetadataEntry) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *ThreatEntryMetadata_MetadataEntry) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
// Describes an individual threat list. A list is defined by three parameters: // Describes an individual threat list. A list is defined by three parameters:
// the type of threat posed, the type of platform targeted by the threat, and // the type of threat posed, the type of platform targeted by the threat, and
// the type of entries in the list. // the type of entries in the list.
@ -781,6 +1028,27 @@ func (m *ThreatListDescriptor) String() string { return proto.Compact
func (*ThreatListDescriptor) ProtoMessage() {} func (*ThreatListDescriptor) ProtoMessage() {}
func (*ThreatListDescriptor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } func (*ThreatListDescriptor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *ThreatListDescriptor) GetThreatType() ThreatType {
if m != nil {
return m.ThreatType
}
return ThreatType_THREAT_TYPE_UNSPECIFIED
}
func (m *ThreatListDescriptor) GetPlatformType() PlatformType {
if m != nil {
return m.PlatformType
}
return PlatformType_PLATFORM_TYPE_UNSPECIFIED
}
func (m *ThreatListDescriptor) GetThreatEntryType() ThreatEntryType {
if m != nil {
return m.ThreatEntryType
}
return ThreatEntryType_THREAT_ENTRY_TYPE_UNSPECIFIED
}
// A collection of lists available for download by the client. // A collection of lists available for download by the client.
type ListThreatListsResponse struct { type ListThreatListsResponse struct {
// The lists available for download by the client. // The lists available for download by the client.
@ -799,30 +1067,6 @@ func (m *ListThreatListsResponse) GetThreatLists() []*ThreatListDescriptor {
return nil return nil
} }
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
type Duration struct {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive.
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
}
func (m *Duration) Reset() { *m = Duration{} }
func (m *Duration) String() string { return proto.CompactTextString(m) }
func (*Duration) ProtoMessage() {}
func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func init() { func init() {
proto.RegisterType((*ThreatInfo)(nil), "safebrowsing_proto.ThreatInfo") proto.RegisterType((*ThreatInfo)(nil), "safebrowsing_proto.ThreatInfo")
proto.RegisterType((*ThreatMatch)(nil), "safebrowsing_proto.ThreatMatch") proto.RegisterType((*ThreatMatch)(nil), "safebrowsing_proto.ThreatMatch")
@ -846,7 +1090,6 @@ func init() {
proto.RegisterType((*ThreatEntryMetadata_MetadataEntry)(nil), "safebrowsing_proto.ThreatEntryMetadata.MetadataEntry") proto.RegisterType((*ThreatEntryMetadata_MetadataEntry)(nil), "safebrowsing_proto.ThreatEntryMetadata.MetadataEntry")
proto.RegisterType((*ThreatListDescriptor)(nil), "safebrowsing_proto.ThreatListDescriptor") proto.RegisterType((*ThreatListDescriptor)(nil), "safebrowsing_proto.ThreatListDescriptor")
proto.RegisterType((*ListThreatListsResponse)(nil), "safebrowsing_proto.ListThreatListsResponse") proto.RegisterType((*ListThreatListsResponse)(nil), "safebrowsing_proto.ListThreatListsResponse")
proto.RegisterType((*Duration)(nil), "safebrowsing_proto.Duration")
proto.RegisterEnum("safebrowsing_proto.ThreatType", ThreatType_name, ThreatType_value) proto.RegisterEnum("safebrowsing_proto.ThreatType", ThreatType_name, ThreatType_value)
proto.RegisterEnum("safebrowsing_proto.PlatformType", PlatformType_name, PlatformType_value) proto.RegisterEnum("safebrowsing_proto.PlatformType", PlatformType_name, PlatformType_value)
proto.RegisterEnum("safebrowsing_proto.CompressionType", CompressionType_name, CompressionType_value) proto.RegisterEnum("safebrowsing_proto.CompressionType", CompressionType_name, CompressionType_value)
@ -854,108 +1097,111 @@ func init() {
proto.RegisterEnum("safebrowsing_proto.FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType", FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_name, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_value) proto.RegisterEnum("safebrowsing_proto.FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType", FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_name, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_value)
} }
func init() { proto.RegisterFile("safebrowsing.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 1628 bytes of a gzipped FileDescriptorProto // 1636 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x58, 0x5b, 0x6f, 0xdb, 0x46, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x8f, 0xdb, 0xc6,
0x16, 0x5e, 0x49, 0xd6, 0xed, 0x48, 0xb2, 0xe5, 0xf1, 0x25, 0x8e, 0x13, 0x27, 0x59, 0x06, 0xd9, 0x15, 0x2f, 0xc5, 0x95, 0xb4, 0xfb, 0xf4, 0x67, 0xb9, 0x63, 0xaf, 0x2d, 0x6f, 0x62, 0x7b, 0x43,
0x0d, 0x8c, 0x85, 0xb1, 0x48, 0x90, 0x64, 0x77, 0xb1, 0xd8, 0x2d, 0x2b, 0x51, 0x36, 0x11, 0x99, 0x23, 0xad, 0x61, 0x14, 0x4a, 0xe1, 0x20, 0x49, 0x0b, 0x14, 0x6d, 0x58, 0x89, 0xf2, 0x12, 0xd1,
0x52, 0x47, 0x72, 0x1c, 0xb7, 0x0f, 0x04, 0x23, 0xd1, 0x31, 0x11, 0xdd, 0x42, 0x52, 0x71, 0xdc, 0x92, 0xca, 0x48, 0xf2, 0xda, 0xcd, 0x81, 0xa0, 0xa9, 0xd1, 0x8a, 0x88, 0x44, 0x2a, 0x9c, 0x91,
0x9f, 0xd0, 0xa7, 0x02, 0x45, 0x9f, 0xdb, 0xe7, 0xbe, 0x16, 0xfd, 0x01, 0xfd, 0x19, 0x7d, 0xec, 0xe5, 0xcd, 0x47, 0x28, 0xd0, 0x53, 0xd1, 0x73, 0x7b, 0xee, 0xb5, 0xe8, 0xb5, 0xdf, 0xa3, 0xc7,
0x2f, 0x28, 0xfa, 0xdc, 0x97, 0x9e, 0x19, 0xce, 0x88, 0xd4, 0xc5, 0x8e, 0x12, 0xfb, 0x21, 0x6f, 0x7e, 0x82, 0xa2, 0x87, 0xde, 0x8b, 0x19, 0x0e, 0x25, 0xea, 0xcf, 0x7a, 0xd7, 0x59, 0x1f, 0x72,
0x33, 0x67, 0xce, 0x7c, 0x73, 0xf8, 0xcd, 0xb9, 0x0d, 0x81, 0x78, 0xd6, 0xb1, 0xfd, 0xc2, 0xed, 0x9b, 0x79, 0x7c, 0xf3, 0x9b, 0xc7, 0xdf, 0xfb, 0x4b, 0x02, 0xa2, 0xde, 0x90, 0xbc, 0x8a, 0xa3,
0x9f, 0x7a, 0x4e, 0xef, 0xe5, 0xce, 0xc0, 0xed, 0xfb, 0x7d, 0x32, 0x26, 0x33, 0xb9, 0x4c, 0xf9, 0x39, 0x0d, 0xc2, 0xf3, 0xfa, 0x34, 0x8e, 0x58, 0x84, 0x56, 0x64, 0xae, 0x90, 0x1d, 0x3d, 0x38,
0x31, 0x0e, 0xd0, 0x3c, 0x71, 0x6d, 0xcb, 0xd7, 0x7b, 0xc7, 0x7d, 0xa2, 0x42, 0xde, 0xe7, 0x33, 0x8f, 0xa2, 0xf3, 0x31, 0xf9, 0x44, 0xec, 0x5e, 0xcd, 0x86, 0x9f, 0x0c, 0x66, 0xb1, 0xc7, 0x82,
0xd3, 0x3f, 0x1b, 0xd8, 0xde, 0x46, 0xec, 0x4e, 0xe2, 0xfe, 0xe2, 0x83, 0x5b, 0x3b, 0xd3, 0x3b, 0x28, 0x4c, 0xce, 0xe8, 0x7f, 0xcf, 0x01, 0xf4, 0x46, 0x31, 0xf1, 0x98, 0x15, 0x0e, 0x23, 0x64,
0x77, 0x82, 0x5d, 0x4d, 0x54, 0xa3, 0x39, 0x7f, 0x34, 0xf6, 0xc8, 0x2e, 0x2c, 0x0e, 0x3a, 0x96, 0x40, 0x99, 0x89, 0x9d, 0xcb, 0x2e, 0xa6, 0x84, 0xd6, 0x94, 0x63, 0xf5, 0x71, 0xf5, 0xe9, 0x83,
0x7f, 0xdc, 0x77, 0xbb, 0x02, 0x24, 0xce, 0x41, 0xee, 0xcc, 0x02, 0xa9, 0x0b, 0x4d, 0x0e, 0x53, 0xfa, 0x26, 0x72, 0x3d, 0x39, 0xd5, 0xbb, 0x98, 0x12, 0x5c, 0x62, 0x8b, 0x35, 0x45, 0xcf, 0xa0,
0x18, 0x44, 0x66, 0x1e, 0xf9, 0x0c, 0x88, 0xb0, 0xc5, 0xee, 0xf9, 0xee, 0x99, 0x00, 0x5b, 0xe0, 0x3a, 0x1d, 0x7b, 0x6c, 0x18, 0xc5, 0x13, 0x09, 0x92, 0x13, 0x20, 0xc7, 0xdb, 0x40, 0x3a, 0x52,
0x60, 0x77, 0xcf, 0xb7, 0x48, 0x63, 0xca, 0x1c, 0xaf, 0xe8, 0x8f, 0x0b, 0x3c, 0x52, 0x81, 0xc5, 0x53, 0xc0, 0x54, 0xa6, 0x99, 0x1d, 0x45, 0x5f, 0x03, 0x92, 0xb6, 0x90, 0x90, 0xc5, 0x17, 0x12,
0x08, 0xa4, 0x83, 0x70, 0x09, 0x84, 0xcb, 0x3d, 0xb8, 0xfd, 0x0e, 0x38, 0x5a, 0x08, 0xa1, 0x70, 0x6c, 0x47, 0x80, 0x3d, 0xba, 0xdc, 0x22, 0x93, 0x2b, 0x0b, 0x3c, 0x8d, 0xad, 0x0a, 0x28, 0x6a,
0x97, 0xf2, 0x73, 0x02, 0x72, 0xc1, 0xf2, 0xbe, 0xe5, 0xb7, 0x4e, 0xc8, 0xff, 0x21, 0x17, 0xa1, 0x41, 0x35, 0x03, 0x19, 0x10, 0x5a, 0x53, 0x8f, 0xd5, 0xc7, 0xa5, 0xa7, 0x0f, 0xaf, 0x80, 0xc3,
0x0d, 0x59, 0x8b, 0xcd, 0xc1, 0x1a, 0x84, 0xac, 0x11, 0x0d, 0x0a, 0x63, 0xa4, 0x21, 0x67, 0xb1, 0x95, 0x25, 0x54, 0x40, 0xa8, 0xfe, 0x4f, 0x15, 0x4a, 0xc9, 0xe3, 0x53, 0x8f, 0xf9, 0x23, 0xf4,
0xb9, 0x38, 0xcb, 0x47, 0x39, 0x23, 0x35, 0x58, 0x9e, 0xa2, 0x6c, 0x23, 0xc5, 0xa1, 0xe6, 0x62, 0x5b, 0x28, 0x65, 0x68, 0xab, 0x29, 0xc7, 0xca, 0x35, 0x58, 0x83, 0x25, 0x6b, 0xc8, 0x84, 0xca,
0x6c, 0x69, 0x82, 0x31, 0xf2, 0x04, 0x52, 0x81, 0x08, 0x89, 0x8a, 0xcd, 0x43, 0x94, 0x50, 0x27, 0x0a, 0x69, 0xb5, 0x9c, 0x80, 0xb8, 0x9a, 0xb3, 0x72, 0x96, 0x33, 0xe4, 0xc0, 0xc1, 0x06, 0x65,
0x5f, 0xc0, 0xda, 0x98, 0x25, 0x5d, 0xdb, 0xb7, 0xda, 0x96, 0x6f, 0xe1, 0xfd, 0x31, 0x9c, 0xbf, 0xb5, 0x82, 0x80, 0xba, 0x16, 0x63, 0xfb, 0x6b, 0x8c, 0xa1, 0x2f, 0xa0, 0x90, 0x88, 0x6a, 0xea,
0xbf, 0x03, 0x67, 0x5f, 0xa8, 0xd3, 0x15, 0x7f, 0x5a, 0x48, 0x4a, 0xb0, 0xd8, 0xb2, 0x5a, 0x27, 0xb1, 0x72, 0x1d, 0xa2, 0xa4, 0x3a, 0xfa, 0x06, 0x0e, 0x57, 0x2c, 0x99, 0x10, 0xe6, 0x0d, 0x3c,
0xb6, 0xd9, 0x1e, 0xba, 0x96, 0xef, 0xf4, 0x7b, 0x1b, 0x49, 0x8e, 0x7a, 0x73, 0x16, 0x6a, 0x59, 0xe6, 0xd5, 0x76, 0x04, 0xce, 0xcf, 0xae, 0xc0, 0x39, 0x95, 0xea, 0xf8, 0x16, 0xdb, 0x14, 0xa2,
0xe8, 0xd0, 0x02, 0xdf, 0x23, 0xa7, 0xca, 0x37, 0x31, 0xd8, 0xa8, 0x38, 0xbd, 0x76, 0xe4, 0x1e, 0x2f, 0xa1, 0xea, 0x7b, 0xfe, 0x88, 0xb8, 0x69, 0x30, 0xd7, 0xf2, 0x02, 0xf5, 0x5e, 0x3d, 0x89,
0x6d, 0x8f, 0xda, 0xaf, 0x87, 0xb6, 0xe7, 0x93, 0xc7, 0x90, 0x6a, 0x75, 0x1c, 0x34, 0x9d, 0xdf, 0xf6, 0x7a, 0x1a, 0xed, 0xf5, 0xa6, 0x54, 0xc0, 0x15, 0x71, 0x20, 0xdd, 0xea, 0x7f, 0x52, 0xa0,
0x65, 0x6e, 0xf6, 0x5d, 0x96, 0xb8, 0x06, 0x8b, 0x1b, 0x2a, 0xb4, 0x23, 0x8e, 0xe0, 0xa0, 0x98, 0xd6, 0x0a, 0xc2, 0x41, 0xc6, 0x89, 0x84, 0x62, 0xf2, 0xdd, 0x8c, 0x50, 0x86, 0x3e, 0x87, 0x82,
0xdf, 0x62, 0xee, 0x22, 0x47, 0xe0, 0x9b, 0x85, 0x23, 0xb0, 0xb1, 0xf2, 0x0c, 0xae, 0xcf, 0x30, 0x3f, 0x0e, 0x48, 0xc8, 0x84, 0x23, 0x4b, 0xdb, 0x1d, 0xd9, 0x10, 0x1a, 0x3c, 0x69, 0xb0, 0xd4,
0xca, 0x1b, 0xf4, 0x7b, 0x9e, 0x4d, 0xfe, 0x0d, 0xe9, 0x6e, 0x20, 0xe2, 0x81, 0x79, 0xe1, 0x75, 0xce, 0x44, 0x41, 0x10, 0x0e, 0x23, 0xe1, 0xc2, 0xd2, 0xdb, 0xa2, 0x40, 0x1c, 0x96, 0x51, 0xc0,
0xf0, 0xbd, 0x54, 0xea, 0x2b, 0x3f, 0xa4, 0x60, 0xab, 0x62, 0xe3, 0x38, 0x58, 0xad, 0x3a, 0x9e, 0xd7, 0xfa, 0x73, 0xb8, 0xb7, 0xc5, 0x28, 0x3a, 0x8d, 0x42, 0x4a, 0xd0, 0xaf, 0xa0, 0x38, 0x49,
0x7f, 0x30, 0x40, 0x32, 0x2f, 0xff, 0xc9, 0x43, 0x58, 0xed, 0x20, 0x9a, 0x39, 0xe4, 0x70, 0xa6, 0x44, 0x22, 0x2b, 0xdf, 0xea, 0x0b, 0x71, 0x16, 0xa7, 0xfa, 0xfa, 0xdf, 0x0a, 0x70, 0xbf, 0x45,
0x1b, 0xc0, 0xc9, 0xc8, 0x2a, 0xcd, 0x42, 0xb9, 0xd0, 0x90, 0x9d, 0x50, 0x24, 0x24, 0x94, 0x74, 0x98, 0x3f, 0x4a, 0x9e, 0xb6, 0x03, 0xca, 0xfa, 0xd3, 0x81, 0xc7, 0x6e, 0xfe, 0xca, 0x33, 0xb8,
0x26, 0x45, 0xde, 0xe6, 0x2f, 0x0b, 0xb0, 0x3c, 0xa5, 0xf9, 0x71, 0x07, 0x62, 0xf2, 0x12, 0x81, 0x3d, 0x0e, 0x28, 0x73, 0x67, 0x02, 0xce, 0x8d, 0x13, 0xb8, 0x34, 0xad, 0x1a, 0xdb, 0x50, 0xde,
0xb8, 0x0a, 0x49, 0xcf, 0xc7, 0x0f, 0xe5, 0x71, 0x98, 0xa7, 0xc1, 0x84, 0xbc, 0x86, 0x5c, 0x0b, 0x6a, 0x48, 0x7d, 0x29, 0x92, 0x12, 0x8c, 0xc6, 0xeb, 0x22, 0x7a, 0xf4, 0xaf, 0x1d, 0x38, 0xd8,
0x3d, 0xc3, 0x77, 0x2d, 0xa7, 0xe7, 0x7b, 0x22, 0xb6, 0x6a, 0x57, 0x40, 0xf9, 0x4e, 0x29, 0x84, 0xd0, 0xfc, 0x71, 0x67, 0x61, 0xfe, 0x06, 0x59, 0x78, 0x1b, 0xf2, 0x94, 0x79, 0x8c, 0x88, 0x24,
0xa5, 0xd1, 0x33, 0x36, 0x7f, 0x8d, 0x41, 0x2e, 0xb2, 0x48, 0xfe, 0x01, 0xa4, 0x6b, 0xbd, 0x95, 0x2c, 0xe3, 0x64, 0x83, 0xbe, 0x83, 0x92, 0x1f, 0x85, 0x94, 0xc5, 0x5e, 0x10, 0x32, 0x2a, 0x13,
0xb7, 0x2f, 0xd3, 0x2a, 0x23, 0x3e, 0x49, 0x8b, 0xb8, 0x12, 0xc0, 0x8a, 0xc4, 0x49, 0xfe, 0x09, 0xcb, 0x79, 0x0f, 0x94, 0xd7, 0x1b, 0x4b, 0x58, 0x9c, 0xbd, 0xe3, 0xe8, 0xdf, 0x0a, 0x94, 0x32,
0xab, 0x4c, 0x9b, 0x45, 0xf1, 0x0b, 0xcb, 0x0b, 0xf5, 0xe3, 0x5c, 0x9f, 0x21, 0x95, 0xc5, 0x92, 0x0f, 0xd1, 0xcf, 0x01, 0x4d, 0xbc, 0x37, 0xa9, 0xf7, 0xd3, 0x9a, 0xca, 0x89, 0xcf, 0x63, 0x6d,
0xdc, 0xb1, 0x0e, 0x29, 0xd7, 0x7e, 0xc9, 0x62, 0x9c, 0x7d, 0x79, 0x96, 0x8a, 0x19, 0xf9, 0x1c, 0xe2, 0xbd, 0x49, 0x60, 0x65, 0xd5, 0x44, 0xbf, 0x80, 0xdb, 0x5c, 0x9b, 0xa7, 0xf0, 0x2b, 0x8f,
0xd6, 0xbd, 0xe1, 0x60, 0xd0, 0x77, 0x7d, 0xbb, 0x6d, 0xb6, 0xfa, 0xdd, 0x81, 0x6b, 0x7b, 0x1e, 0x2e, 0xf5, 0x73, 0x42, 0x9f, 0x23, 0x35, 0xe5, 0xa3, 0xf4, 0xc4, 0x1d, 0x28, 0xc4, 0xe4, 0x9c,
0x2e, 0x5c, 0x58, 0x21, 0x4a, 0xa1, 0x1e, 0xa7, 0x79, 0x6d, 0x04, 0x11, 0x59, 0xf1, 0x94, 0xaf, 0x27, 0x38, 0x7f, 0xf3, 0x3d, 0x2c, 0x77, 0xe8, 0xf7, 0x70, 0x87, 0xce, 0xa6, 0xd3, 0x28, 0x66,
0xd3, 0x70, 0xeb, 0x3c, 0xc2, 0x44, 0x28, 0x9e, 0xc1, 0xda, 0xb8, 0xd7, 0x07, 0x72, 0x19, 0x98, 0x64, 0xe0, 0xfa, 0xd1, 0x64, 0x1a, 0x13, 0x4a, 0x83, 0x28, 0x7c, 0x6b, 0x7b, 0x68, 0x2c, 0xf5,
0xda, 0xfb, 0xdc, 0x41, 0xb0, 0x75, 0xec, 0x12, 0x02, 0x11, 0x5d, 0xe9, 0x4c, 0xc9, 0x3c, 0x52, 0x04, 0xcd, 0x87, 0x0b, 0x88, 0xcc, 0x13, 0xaa, 0xff, 0xb1, 0x08, 0x0f, 0x2e, 0x23, 0x4c, 0xa6,
0x87, 0xb5, 0xae, 0xd3, 0x73, 0xba, 0xc3, 0xae, 0x79, 0x6a, 0x39, 0x7e, 0x98, 0x04, 0xe3, 0x73, 0xe2, 0x05, 0x1c, 0xae, 0x46, 0x7d, 0x22, 0x4f, 0x13, 0xd3, 0x7c, 0x17, 0x1f, 0x24, 0x47, 0x57,
0x24, 0xc1, 0x15, 0xb1, 0xf5, 0x10, 0x77, 0x4a, 0xe1, 0xe6, 0xf7, 0x49, 0x20, 0xd3, 0xa7, 0x5f, 0x9c, 0x90, 0x88, 0xf0, 0xad, 0xf1, 0x86, 0x8c, 0xa2, 0x53, 0x38, 0x9c, 0x04, 0x61, 0x30, 0x99,
0x3e, 0x98, 0x66, 0x46, 0x41, 0xfc, 0x12, 0x51, 0x30, 0x15, 0x9d, 0x89, 0x0f, 0x8a, 0xce, 0x53, 0x4d, 0xdc, 0xb9, 0x17, 0xb0, 0x65, 0x05, 0xcc, 0x5d, 0x55, 0x01, 0x6f, 0xc9, 0x73, 0x67, 0x5e,
0x28, 0xc8, 0x0b, 0x0b, 0x60, 0x16, 0x38, 0x0c, 0xbd, 0x92, 0x4b, 0xdb, 0x91, 0x83, 0xe0, 0x60, 0xc0, 0x52, 0xe1, 0xd1, 0x5f, 0xf3, 0x80, 0x36, 0xaf, 0xbe, 0x79, 0x26, 0x6d, 0x4d, 0x81, 0xdc,
0x37, 0x32, 0x23, 0x9f, 0x40, 0xd6, 0x6a, 0xb7, 0x1d, 0x9f, 0xfb, 0x69, 0x92, 0x7b, 0x8a, 0xf2, 0x0d, 0x52, 0x60, 0x23, 0x35, 0xd5, 0x1f, 0x94, 0x9a, 0x73, 0xa8, 0xa4, 0xde, 0x4a, 0x60, 0x76,
0x0e, 0x22, 0x1a, 0xb6, 0x4f, 0xc3, 0x4d, 0xe4, 0x7f, 0x90, 0x71, 0xed, 0x6e, 0xff, 0x8d, 0xd5, 0x04, 0x0c, 0x7e, 0x2f, 0x1e, 0xab, 0xa7, 0x8b, 0xe4, 0xe2, 0x38, 0xb3, 0x43, 0x5f, 0xc2, 0x9e,
0xf1, 0xb0, 0xb0, 0xcf, 0x0b, 0x30, 0xda, 0x43, 0xee, 0x43, 0xb1, 0x67, 0x9f, 0x9a, 0x41, 0xee, 0x37, 0x18, 0x04, 0x4c, 0x04, 0x69, 0x5e, 0x84, 0x89, 0x7e, 0x05, 0x11, 0x5d, 0xc2, 0xf0, 0xf2,
0x36, 0x83, 0x94, 0x92, 0xe6, 0x29, 0x65, 0x11, 0xe5, 0x41, 0x7a, 0x6f, 0xf0, 0xdc, 0xf2, 0x2f, 0x10, 0xfa, 0x0d, 0xec, 0xc6, 0x64, 0x12, 0xbd, 0xf6, 0xc6, 0xb4, 0x56, 0xb8, 0x36, 0xc0, 0xe2,
0xc8, 0x60, 0xe5, 0x68, 0xbd, 0xf2, 0x86, 0xdd, 0x8d, 0xcc, 0xf9, 0x9e, 0x55, 0x12, 0x3a, 0x74, 0x0c, 0x7a, 0x0c, 0x5a, 0x48, 0xe6, 0x6e, 0x52, 0xb8, 0xdd, 0xa4, 0x9e, 0x14, 0x45, 0x3d, 0xa9,
0xa4, 0xad, 0x50, 0xc8, 0x47, 0x39, 0x20, 0x5b, 0x70, 0x9d, 0x6a, 0x8d, 0x7a, 0xcd, 0x68, 0x68, 0x86, 0x64, 0x9e, 0xd4, 0xf6, 0xae, 0x28, 0x2c, 0xbf, 0x84, 0x5d, 0x7f, 0x44, 0xfc, 0x6f, 0xe9,
0x66, 0xf3, 0xa8, 0xae, 0x99, 0x07, 0x46, 0xa3, 0xae, 0x95, 0xf4, 0x8a, 0xae, 0x95, 0x8b, 0x7f, 0x6c, 0x52, 0xdb, 0x15, 0x61, 0xf5, 0xe1, 0xd6, 0x7c, 0x92, 0x3a, 0x78, 0xa1, 0xad, 0x63, 0x28,
0x21, 0x04, 0x16, 0xeb, 0x2a, 0x6d, 0xea, 0x6a, 0xd5, 0x3c, 0xa8, 0x97, 0xd5, 0xa6, 0x56, 0x8c, 0x67, 0x39, 0x40, 0xf7, 0xe1, 0x1e, 0x36, 0xbb, 0x1d, 0xc7, 0xee, 0x9a, 0x6e, 0xef, 0x65, 0xc7,
0x91, 0x25, 0xc8, 0x55, 0x0e, 0xaa, 0x23, 0x41, 0x5c, 0xf9, 0x29, 0x06, 0x6b, 0xac, 0x30, 0x56, 0x74, 0xfb, 0x76, 0xb7, 0x63, 0x36, 0xac, 0x96, 0x65, 0x36, 0xb5, 0x9f, 0x20, 0x04, 0xd5, 0x8e,
0x86, 0x9d, 0xce, 0x9e, 0xe5, 0x5d, 0x41, 0xa9, 0xbe, 0x0b, 0x85, 0x28, 0x0b, 0x41, 0x9b, 0x9a, 0x81, 0x7b, 0x96, 0xd1, 0x76, 0xfb, 0x9d, 0xa6, 0xd1, 0x33, 0x35, 0x05, 0xed, 0x43, 0xa9, 0xd5,
0xa7, 0xf9, 0x56, 0xc8, 0x81, 0x37, 0x59, 0xcf, 0x13, 0xef, 0x5d, 0xcf, 0xff, 0x88, 0xc1, 0xfa, 0x6f, 0x2f, 0x04, 0x39, 0xfd, 0x1f, 0x0a, 0x1c, 0xf2, 0xae, 0xd8, 0x9a, 0x8d, 0xc7, 0x27, 0x1e,
0xa4, 0xdd, 0x97, 0xae, 0xe6, 0x57, 0x9f, 0x02, 0x48, 0x13, 0xae, 0xf5, 0xec, 0x97, 0x38, 0x7e, 0x7d, 0x0f, 0x7d, 0xfa, 0x11, 0x54, 0xb2, 0x2c, 0x24, 0x03, 0x6a, 0x19, 0x97, 0xfd, 0x25, 0x07,
0x63, 0x9b, 0x13, 0xbd, 0x55, 0x62, 0x0e, 0xcc, 0x35, 0xb9, 0xb9, 0x34, 0xd6, 0x63, 0xd5, 0x01, 0x74, 0xbd, 0x99, 0xab, 0xef, 0xdc, 0xcc, 0xff, 0xa7, 0xc0, 0x9d, 0x75, 0xbb, 0x6f, 0xdc, 0xca,
0x42, 0xe6, 0xc9, 0x0d, 0xc8, 0x0a, 0xc6, 0x9d, 0x36, 0xbf, 0xac, 0x2c, 0x3a, 0x4d, 0xb0, 0xdc, 0xdf, 0x73, 0xfe, 0xa3, 0xaf, 0xe1, 0x6e, 0x48, 0xce, 0x3d, 0x16, 0xbc, 0x26, 0xee, 0xda, 0x48,
0x26, 0xf7, 0xb0, 0xa7, 0x0b, 0x16, 0xdf, 0xd8, 0xae, 0x27, 0xbf, 0x25, 0x4b, 0xc5, 0x25, 0x3d, 0xa5, 0x5e, 0x05, 0x78, 0x98, 0x9e, 0x6c, 0xac, 0x8c, 0x56, 0x1d, 0x80, 0x25, 0xe7, 0xe8, 0x03,
0x0b, 0x84, 0x8a, 0x02, 0x19, 0xe9, 0x71, 0xac, 0x34, 0x78, 0x27, 0xd6, 0x83, 0x47, 0x8f, 0x39, 0xd8, 0x93, 0x5c, 0x07, 0x03, 0xe1, 0xa6, 0x3d, 0xbc, 0x9b, 0x08, 0xac, 0x01, 0xfa, 0x18, 0xaa,
0x58, 0x9e, 0x8a, 0x99, 0xf2, 0x50, 0x36, 0xe7, 0xdc, 0xff, 0xd1, 0xbf, 0x16, 0x4e, 0x90, 0x79, 0xf2, 0xe1, 0x6b, 0x12, 0xd3, 0xf4, 0x2d, 0xf6, 0xb0, 0x74, 0xcf, 0xf3, 0x44, 0xa8, 0xeb, 0xb0,
0xa1, 0xc4, 0xc7, 0xa4, 0x08, 0x89, 0xa1, 0xdb, 0x11, 0x47, 0xb0, 0xa1, 0xf2, 0x7b, 0x1c, 0x16, 0x9b, 0xc6, 0x1a, 0xef, 0x08, 0x74, 0xe4, 0x3d, 0xfd, 0xec, 0x73, 0x01, 0x56, 0xc6, 0x72, 0xa7,
0xc7, 0xa3, 0x86, 0x18, 0x50, 0x8c, 0x14, 0x96, 0x68, 0x12, 0x9c, 0xab, 0xb8, 0x2c, 0xb5, 0xc6, 0x7f, 0x9a, 0x0e, 0xe4, 0x22, 0xf2, 0x11, 0x82, 0x9d, 0x91, 0x47, 0x47, 0x52, 0x49, 0xac, 0x91,
0x05, 0xe4, 0xbf, 0x00, 0xae, 0x75, 0x6a, 0x9e, 0x70, 0x37, 0x10, 0x57, 0xb5, 0x35, 0x0b, 0x89, 0x06, 0xea, 0x2c, 0x1e, 0xcb, 0x2b, 0xf8, 0x52, 0xff, 0x6f, 0x0e, 0xaa, 0xab, 0xf9, 0x82, 0x6c,
0x5a, 0xa7, 0xc2, 0x57, 0xb2, 0xae, 0x1c, 0x32, 0x57, 0x64, 0xbb, 0xd1, 0x97, 0x9c, 0x16, 0x7f, 0xd0, 0x32, 0xfd, 0x24, 0x5b, 0xfe, 0xae, 0xd5, 0x53, 0xf6, 0xfd, 0x55, 0x01, 0xfa, 0x35, 0x40,
0xb8, 0x9c, 0xeb, 0x8a, 0xb8, 0x5d, 0x0f, 0xb4, 0x28, 0x3b, 0x50, 0x8c, 0xf1, 0xf1, 0x93, 0x73, 0xec, 0xcd, 0xdd, 0x91, 0x08, 0x00, 0xe9, 0xa4, 0xfb, 0xdb, 0x90, 0xb0, 0x37, 0x97, 0x51, 0xb2,
0x71, 0x20, 0xcf, 0x0f, 0x9a, 0x85, 0x7b, 0x33, 0x01, 0x50, 0xad, 0x6c, 0x77, 0x7c, 0x4b, 0xeb, 0x17, 0xa7, 0x4b, 0x1e, 0x84, 0xfc, 0x74, 0x10, 0x0e, 0x02, 0x5f, 0x7c, 0xac, 0x5c, 0x1a, 0x84,
0xb5, 0xfa, 0x6d, 0x94, 0x23, 0x0e, 0x8a, 0x84, 0x21, 0x7b, 0x90, 0xe7, 0x38, 0xd2, 0x92, 0xe4, 0xd8, 0x9b, 0x5b, 0x89, 0x16, 0xe6, 0x17, 0xca, 0x35, 0x6a, 0x41, 0x29, 0x0e, 0x7c, 0x92, 0xde,
0xfb, 0x00, 0x71, 0x13, 0x84, 0x45, 0xca, 0xdf, 0x00, 0x42, 0x5b, 0xc9, 0x06, 0xa4, 0x25, 0x24, 0x9f, 0xcc, 0x08, 0x1f, 0x6f, 0x05, 0x08, 0x7c, 0xd2, 0x24, 0x63, 0xe6, 0x99, 0xa1, 0x1f, 0x0d,
0x8b, 0x87, 0x24, 0x95, 0x53, 0xe5, 0x29, 0x64, 0x47, 0x94, 0x90, 0xdb, 0x90, 0x43, 0x56, 0x8f, 0x82, 0xf0, 0x1c, 0x03, 0x3f, 0x29, 0x0d, 0x39, 0x81, 0xb2, 0xc0, 0x49, 0x2d, 0xc9, 0xbf, 0x0b,
0x9d, 0xb7, 0xa6, 0xe7, 0x7c, 0x69, 0x8b, 0x4e, 0x03, 0x02, 0x51, 0x03, 0x25, 0x98, 0x6e, 0x26, 0x90, 0x30, 0x41, 0x5a, 0xa4, 0xff, 0x14, 0x60, 0x69, 0x2b, 0xaa, 0x41, 0x31, 0x85, 0xe4, 0x99,
0x69, 0xce, 0x47, 0x78, 0x54, 0xbe, 0x8b, 0xc1, 0xf2, 0x94, 0x5d, 0x0c, 0xf5, 0xd8, 0x71, 0xb1, 0x90, 0xc7, 0xe9, 0x56, 0xff, 0x0a, 0xf6, 0x16, 0x94, 0xa0, 0x87, 0x50, 0x9a, 0xc6, 0x64, 0x18,
0xa0, 0x63, 0x96, 0x1c, 0x06, 0xa8, 0x09, 0x0a, 0x5c, 0xf4, 0x8c, 0x49, 0x98, 0x7f, 0xf2, 0xaf, 0xbc, 0x71, 0x69, 0xf0, 0x3d, 0x91, 0x03, 0x06, 0x24, 0xa2, 0x6e, 0xf0, 0x3d, 0x2f, 0x34, 0xeb,
0x1e, 0x58, 0xae, 0x85, 0x8f, 0x19, 0xdb, 0x15, 0x3d, 0x4b, 0x81, 0x49, 0xeb, 0x52, 0xc8, 0x70, 0x34, 0x97, 0x33, 0x3c, 0xea, 0x7f, 0x51, 0xe0, 0x60, 0xc3, 0x2e, 0x8e, 0x3a, 0x0c, 0x62, 0xca,
0x7a, 0x18, 0x95, 0xe1, 0xf3, 0x92, 0x5b, 0x87, 0x22, 0xd9, 0xcf, 0xfc, 0x15, 0xf2, 0x36, 0x3b, 0xdc, 0xd7, 0xde, 0x78, 0x96, 0xa0, 0xaa, 0x18, 0x84, 0xe8, 0x39, 0x97, 0xf0, 0xf8, 0x14, 0x6f,
0x14, 0xbb, 0x96, 0xd1, 0x7b, 0x28, 0x4f, 0x73, 0x42, 0xc6, 0xba, 0x1f, 0x66, 0xe1, 0xca, 0x8c, 0x3d, 0xf5, 0x62, 0x6f, 0x42, 0x18, 0x89, 0xe5, 0xa8, 0x52, 0xe1, 0xd2, 0x4e, 0x2a, 0xe4, 0x38,
0xb7, 0x10, 0x96, 0xd3, 0x74, 0xd8, 0x5f, 0xb1, 0x84, 0xf1, 0x68, 0xce, 0x57, 0xd4, 0x8e, 0x1c, 0xe1, 0x6c, 0x92, 0xf9, 0xa4, 0x14, 0xd6, 0x85, 0xb3, 0x49, 0x3a, 0xc6, 0x7c, 0x04, 0x65, 0xc2,
0x04, 0x6f, 0x34, 0x89, 0xb2, 0xf9, 0x04, 0x0a, 0x63, 0x2b, 0x2c, 0x2c, 0x5e, 0xd9, 0x67, 0x22, 0x2f, 0x25, 0x03, 0x77, 0xf1, 0x0d, 0x54, 0xc6, 0x25, 0x29, 0xe3, 0x43, 0x0f, 0xb7, 0xf0, 0xd6,
0x52, 0xd8, 0x90, 0xf5, 0x9d, 0x01, 0x23, 0x01, 0x8f, 0xc1, 0x44, 0xf9, 0x2d, 0x06, 0xab, 0x61, 0x96, 0xef, 0x1f, 0xe4, 0x40, 0x71, 0x39, 0x56, 0xf1, 0x52, 0xf1, 0xd9, 0x35, 0xbf, 0x9c, 0xea,
0x4d, 0x2c, 0xdb, 0x5e, 0xcb, 0x75, 0x06, 0x7e, 0xdf, 0xfd, 0xb8, 0xfb, 0xef, 0xc4, 0x87, 0x77, 0xe9, 0x22, 0xf9, 0x2e, 0x4b, 0x51, 0x8e, 0xbe, 0x80, 0xca, 0xca, 0x13, 0x9e, 0x16, 0xdf, 0x92,
0x1e, 0xca, 0x31, 0x5c, 0x63, 0x9f, 0x1a, 0x7e, 0x74, 0x98, 0xc7, 0x9f, 0x8e, 0xfe, 0x99, 0xb0, 0x0b, 0x99, 0x29, 0x7c, 0xc9, 0xc7, 0xcd, 0x84, 0x91, 0x84, 0xc7, 0x64, 0xa3, 0xff, 0x47, 0x81,
0x6e, 0x4d, 0xde, 0xcd, 0xfd, 0xf3, 0x8f, 0x19, 0xe7, 0x4c, 0xfe, 0x3d, 0xe1, 0xa0, 0xca, 0x7f, 0xdb, 0xcb, 0x6e, 0xd8, 0x24, 0xd4, 0x8f, 0x83, 0x29, 0x8b, 0xe2, 0x1f, 0xf7, 0xd8, 0xad, 0xfe,
0x20, 0x33, 0xca, 0xc9, 0x18, 0x10, 0x9e, 0x8d, 0xbd, 0x77, 0xdb, 0x13, 0xfe, 0x28, 0xa7, 0xec, 0xf0, 0x99, 0x43, 0x1f, 0xc2, 0x5d, 0xfe, 0xaa, 0xcb, 0x97, 0x5e, 0x56, 0xf0, 0xaf, 0x16, 0xff,
0x56, 0x7a, 0x56, 0xaf, 0x2f, 0xfb, 0xe6, 0x60, 0xb2, 0xfd, 0x55, 0x4c, 0xfe, 0xcb, 0xe1, 0x1c, 0x49, 0xf8, 0x90, 0x96, 0xfa, 0xe6, 0xf1, 0xe5, 0xd7, 0xac, 0x72, 0x96, 0xfe, 0x31, 0x11, 0xa0,
0xdc, 0x80, 0x6b, 0xcd, 0x3d, 0xaa, 0xa9, 0xcd, 0x59, 0x45, 0x37, 0x07, 0xe9, 0x7d, 0xb5, 0x7a, 0x4f, 0xfe, 0xa0, 0xa4, 0xff, 0x60, 0xc4, 0x7b, 0x7c, 0x00, 0x77, 0x7b, 0x27, 0xd8, 0x34, 0x7a,
0xa8, 0x52, 0x56, 0x6d, 0xd7, 0x81, 0x34, 0x6a, 0x25, 0x56, 0x80, 0x35, 0x63, 0x57, 0x37, 0x34, 0xdb, 0x5a, 0x66, 0x09, 0x8a, 0xa7, 0x46, 0xfb, 0xcc, 0xc0, 0xbc, 0x57, 0xde, 0x01, 0xd4, 0x75,
0x8d, 0xea, 0xc6, 0x6e, 0x31, 0x4e, 0xd6, 0x60, 0xf9, 0xc0, 0x38, 0x54, 0x8d, 0xa6, 0x56, 0x36, 0x1a, 0xbc, 0x7d, 0x9a, 0xf6, 0x33, 0xcb, 0x36, 0x4d, 0x6c, 0xd9, 0xcf, 0xb4, 0x1c, 0x3a, 0x84,
0x1b, 0xb5, 0x4a, 0x93, 0xab, 0x27, 0xb0, 0x72, 0xde, 0xae, 0xd7, 0x9a, 0x9a, 0xc1, 0x4a, 0x76, 0x83, 0xbe, 0x7d, 0x66, 0xd8, 0x3d, 0xb3, 0xe9, 0x76, 0x9d, 0x56, 0x4f, 0xa8, 0xab, 0xe8, 0x11,
0xf5, 0xc8, 0xdc, 0x53, 0xe9, 0x3e, 0x16, 0x6b, 0x53, 0xad, 0xd7, 0xab, 0x7a, 0x49, 0x6d, 0xea, 0x3c, 0xec, 0x38, 0x3d, 0xd3, 0xe6, 0x0d, 0xb7, 0xfd, 0xd2, 0x3d, 0x31, 0xf0, 0x69, 0xab, 0xdf,
0x35, 0xa3, 0xb8, 0xb0, 0xfd, 0x6d, 0x0c, 0xf2, 0xd1, 0x0b, 0x62, 0x5d, 0x40, 0xbd, 0xaa, 0x36, 0x76, 0x8d, 0x4e, 0xa7, 0x6d, 0x35, 0x8c, 0x9e, 0xe5, 0xd8, 0xda, 0xce, 0x93, 0x3f, 0x2b, 0x50,
0x2b, 0x35, 0xba, 0x7f, 0x8e, 0x41, 0x87, 0xba, 0x51, 0xae, 0x1d, 0x36, 0xd0, 0xa0, 0x2c, 0x24, 0xce, 0x92, 0xcc, 0x7b, 0x78, 0xa7, 0x6d, 0xf4, 0x5a, 0x0e, 0x3e, 0xbd, 0xc4, 0xa0, 0x33, 0xcb,
0xab, 0xba, 0x71, 0xf0, 0x1c, 0x6d, 0x40, 0xb9, 0x6a, 0x94, 0x69, 0x4d, 0x2f, 0xe3, 0xc9, 0x69, 0x6e, 0x3a, 0x67, 0x5d, 0x4d, 0x41, 0x7b, 0x90, 0x6f, 0x5b, 0x76, 0xff, 0x85, 0x96, 0xe3, 0x72,
0x48, 0xd4, 0x1a, 0xcf, 0x8b, 0x0b, 0x6c, 0xa0, 0xd7, 0x1a, 0xc5, 0x24, 0x7a, 0x6c, 0x5e, 0x35, 0xc3, 0x6e, 0x62, 0xc7, 0x6a, 0x6a, 0x2a, 0x2a, 0x82, 0xea, 0x74, 0x5f, 0x68, 0x3b, 0x7c, 0x61,
0x8e, 0x4c, 0x89, 0x5c, 0x4c, 0x91, 0x65, 0x28, 0xa0, 0x59, 0x23, 0x49, 0xa3, 0x98, 0x26, 0x00, 0x39, 0x5d, 0x2d, 0x8f, 0x34, 0x28, 0x1b, 0xf6, 0x4b, 0x37, 0x45, 0xd6, 0x0a, 0xe8, 0x00, 0x2a,
0xa9, 0xd2, 0x1e, 0xad, 0xed, 0x6b, 0xc5, 0xcc, 0x76, 0x05, 0x96, 0x26, 0x12, 0x35, 0xb9, 0x03, 0x46, 0xbb, 0xbd, 0x90, 0x74, 0xb5, 0x22, 0x02, 0x28, 0x34, 0x4e, 0xb0, 0x73, 0x6a, 0x6a, 0xbb,
0x37, 0x4b, 0xb5, 0xfd, 0x3a, 0xf6, 0x28, 0x0d, 0xb4, 0x7d, 0x96, 0x71, 0x78, 0x1c, 0x55, 0x0f, 0x4f, 0x5a, 0xb0, 0xbf, 0x56, 0x6c, 0xd1, 0x31, 0x7c, 0xd8, 0x70, 0x4e, 0x3b, 0xd8, 0xec, 0x76,
0xd1, 0xb0, 0x0c, 0x2c, 0x50, 0xbd, 0x84, 0x0d, 0xc9, 0xb6, 0x03, 0x4b, 0x13, 0x4e, 0x83, 0xa1, 0x2d, 0xc7, 0xde, 0x66, 0x5c, 0x11, 0x54, 0x6c, 0x9c, 0x69, 0x0a, 0xda, 0x85, 0x1d, 0x6c, 0x35,
0xbd, 0x25, 0x08, 0x47, 0x6e, 0xe8, 0xd1, 0x39, 0xbd, 0xce, 0x01, 0x45, 0x9a, 0x9f, 0xcb, 0xd3, 0x4c, 0x2d, 0xf7, 0xe4, 0x1b, 0xd8, 0x5f, 0x73, 0x3c, 0xfa, 0x08, 0xee, 0x4b, 0xc2, 0x4d, 0xbb,
0x10, 0x13, 0x0d, 0xfe, 0x54, 0x37, 0x54, 0xdc, 0x50, 0xd6, 0x77, 0xb5, 0x46, 0x13, 0x3f, 0x3a, 0x87, 0x5f, 0x5e, 0x02, 0xd4, 0xc7, 0x6d, 0x4d, 0x41, 0x55, 0x00, 0xf3, 0x85, 0xd9, 0xe8, 0xf7,
0x0f, 0x19, 0xbd, 0x6e, 0x52, 0xd5, 0xd8, 0x45, 0xbe, 0x5f, 0xa4, 0xb8, 0xf3, 0x3c, 0xfc, 0x33, 0x8c, 0xdf, 0xb5, 0x4d, 0x2d, 0x87, 0xca, 0xb0, 0x6b, 0x75, 0x5c, 0x6c, 0xd8, 0xcf, 0x4c, 0x4d,
0x00, 0x00, 0xff, 0xff, 0x08, 0xe3, 0x60, 0x1f, 0xd4, 0x13, 0x00, 0x00, 0x7d, 0x55, 0x10, 0x2e, 0xff, 0xf4, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x68, 0x6b, 0x92, 0x80,
0x9e, 0x13, 0x00, 0x00,
} }

View File

@ -14,6 +14,8 @@
syntax = "proto3"; syntax = "proto3";
import "google/protobuf/duration.proto";
package safebrowsing_proto; package safebrowsing_proto;
// The Safe Browsing APIs enable clients to check web resources (most commonly // The Safe Browsing APIs enable clients to check web resources (most commonly
@ -81,7 +83,7 @@ message ThreatMatch {
// The cache lifetime for the returned match. Clients must not cache this // The cache lifetime for the returned match. Clients must not cache this
// response for more than this duration to avoid false positives. // response for more than this duration to avoid false positives.
Duration cache_duration = 5; google.protobuf.Duration cache_duration = 5;
} }
// Request to check entries against lists. // Request to check entries against lists.
@ -208,7 +210,7 @@ message FetchThreatListUpdatesResponse {
// The minimum duration the client must wait before issuing any update // The minimum duration the client must wait before issuing any update
// request. If this field is not set clients may update as soon as they want. // request. If this field is not set clients may update as soon as they want.
Duration minimum_wait_duration = 2; google.protobuf.Duration minimum_wait_duration = 2;
} }
// Request to return full hashes matched by the provided hash prefixes. // Request to return full hashes matched by the provided hash prefixes.
@ -231,11 +233,11 @@ message FindFullHashesResponse {
// The minimum duration the client must wait before issuing any find hashes // The minimum duration the client must wait before issuing any find hashes
// request. If this field is not set, clients can issue a request as soon as // request. If this field is not set, clients can issue a request as soon as
// they want. // they want.
Duration minimum_wait_duration = 2; google.protobuf.Duration minimum_wait_duration = 2;
// For requested entities that did not match the threat list, how long to // For requested entities that did not match the threat list, how long to
// cache the response. // cache the response.
Duration negative_cache_duration = 3; google.protobuf.Duration negative_cache_duration = 3;
} }
@ -444,24 +446,3 @@ message ListThreatListsResponse {
// The lists available for download by the client. // The lists available for download by the client.
repeated ThreatListDescriptor threat_lists = 1; repeated ThreatListDescriptor threat_lists = 1;
} }
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
message Duration {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive.
int64 seconds = 1;
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
int32 nanos = 2;
}

View File

@ -72,6 +72,7 @@
package safebrowsing package safebrowsing
import ( import (
"context"
"errors" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
@ -94,6 +95,10 @@ const (
// strings to send with every API call. // strings to send with every API call.
DefaultID = "GoSafeBrowser" DefaultID = "GoSafeBrowser"
DefaultVersion = "1.0.0" DefaultVersion = "1.0.0"
// DefaultRequestTimeout is the default amount of time a single
// api request can take.
DefaultRequestTimeout = time.Minute
) )
// Errors specific to this package. // Errors specific to this package.
@ -207,6 +212,9 @@ type Config struct {
// If empty, it defaults to DefaultThreatLists. // If empty, it defaults to DefaultThreatLists.
ThreatLists []ThreatDescriptor ThreatLists []ThreatDescriptor
// RequestTimeout determines the timeout value for the http client.
RequestTimeout time.Duration
// Logger is an io.Writer that allows SafeBrowser to write debug information // Logger is an io.Writer that allows SafeBrowser to write debug information
// intended for human consumption. // intended for human consumption.
// If empty, no logs will be written. // If empty, no logs will be written.
@ -231,6 +239,9 @@ func (c *Config) setDefaults() bool {
if c.UpdatePeriod <= 0 { if c.UpdatePeriod <= 0 {
c.UpdatePeriod = DefaultUpdatePeriod c.UpdatePeriod = DefaultUpdatePeriod
} }
if c.RequestTimeout <= 0 {
c.RequestTimeout = DefaultRequestTimeout
}
if c.compressionTypes == nil { if c.compressionTypes == nil {
c.compressionTypes = []pb.CompressionType{pb.CompressionType_RAW, pb.CompressionType_RICE} c.compressionTypes = []pb.CompressionType{pb.CompressionType_RAW, pb.CompressionType_RICE}
} }
@ -251,8 +262,8 @@ func (c Config) copy() Config {
// local database and caching that would normally be needed to interact // local database and caching that would normally be needed to interact
// with the API server. // with the API server.
type SafeBrowser struct { type SafeBrowser struct {
stats Stats // Must be first for 64-bit alignment on non 64-bit systems.
config Config config Config
stats Stats
api api api api
db database db database
c cache c cache
@ -267,10 +278,11 @@ type SafeBrowser struct {
// Stats records statistics regarding SafeBrowser's operation. // Stats records statistics regarding SafeBrowser's operation.
type Stats struct { type Stats struct {
QueriesByDatabase int64 // Number of queries satisfied by the database alone QueriesByDatabase int64 // Number of queries satisfied by the database alone
QueriesByCache int64 // Number of queries satisfied by the cache alone QueriesByCache int64 // Number of queries satisfied by the cache alone
QueriesByAPI int64 // Number of queries satisfied by an API call QueriesByAPI int64 // Number of queries satisfied by an API call
QueriesFail int64 // Number of queries that could not be satisfied QueriesFail int64 // Number of queries that could not be satisfied
DatabaseUpdateLag time.Duration // Duration since last *missed* update. 0 if next update is in the future.
} }
// NewSafeBrowser creates a new SafeBrowser. // NewSafeBrowser creates a new SafeBrowser.
@ -286,7 +298,7 @@ func NewSafeBrowser(conf Config) (*SafeBrowser, error) {
// Create the SafeBrowsing object. // Create the SafeBrowsing object.
if conf.api == nil { if conf.api == nil {
var err error var err error
conf.api, err = newNetAPI(conf.ServerURL, conf.APIKey) conf.api, err = newNetAPI(conf.ServerURL, conf.APIKey, conf.RequestTimeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -316,14 +328,19 @@ func NewSafeBrowser(conf Config) (*SafeBrowser, error) {
} }
sb.log = log.New(w, "safebrowsing: ", log.Ldate|log.Ltime|log.Lshortfile) sb.log = log.New(w, "safebrowsing: ", log.Ldate|log.Ltime|log.Lshortfile)
delay := time.Duration(0)
// If database file is provided, use that to initialize. // If database file is provided, use that to initialize.
if !sb.db.Init(&sb.config, sb.log) { if !sb.db.Init(&sb.config, sb.log) {
sb.db.Update(sb.api) delay, _ = sb.db.Update(sb.api)
} else {
if age := sb.db.SinceLastUpdate(); age < sb.config.UpdatePeriod {
delay = sb.config.UpdatePeriod - age
}
} }
// Start the background list updater. // Start the background list updater.
sb.done = make(chan bool) sb.done = make(chan bool)
go sb.updater(conf.UpdatePeriod) go sb.updater(delay)
return sb, nil return sb, nil
} }
@ -337,10 +354,28 @@ func (sb *SafeBrowser) Status() (Stats, error) {
QueriesByCache: atomic.LoadInt64(&sb.stats.QueriesByCache), QueriesByCache: atomic.LoadInt64(&sb.stats.QueriesByCache),
QueriesByAPI: atomic.LoadInt64(&sb.stats.QueriesByAPI), QueriesByAPI: atomic.LoadInt64(&sb.stats.QueriesByAPI),
QueriesFail: atomic.LoadInt64(&sb.stats.QueriesFail), QueriesFail: atomic.LoadInt64(&sb.stats.QueriesFail),
DatabaseUpdateLag: sb.db.UpdateLag(),
} }
return stats, sb.db.Status() return stats, sb.db.Status()
} }
// WaitUntilReady blocks until the database is not in an error state.
// Returns nil when the database is ready. Returns an error if the provided
// context is canceled or if the SafeBrowser instance is Closed.
func (sb *SafeBrowser) WaitUntilReady(ctx context.Context) error {
if atomic.LoadUint32(&sb.closed) == 1 {
return errClosed
}
select {
case <-sb.db.Ready():
return nil
case <-ctx.Done():
return ctx.Err()
case <-sb.done:
return errClosed
}
}
// LookupURLs looks up the provided URLs. It returns a list of threats, one for // LookupURLs looks up the provided URLs. It returns a list of threats, one for
// every URL requested, and an error if any occurred. It is safe to call this // every URL requested, and an error if any occurred. It is safe to call this
// method concurrently. // method concurrently.
@ -492,16 +527,17 @@ func (sb *SafeBrowser) LookupURLs(urls []string) (threats [][]URLThreat, err err
// updater is a blocking method that periodically updates the local database. // updater is a blocking method that periodically updates the local database.
// This should be run as a separate goroutine and will be automatically stopped // This should be run as a separate goroutine and will be automatically stopped
// when sb.Close is called. // when sb.Close is called.
func (sb *SafeBrowser) updater(period time.Duration) { func (sb *SafeBrowser) updater(delay time.Duration) {
ticker := time.NewTicker(period)
defer ticker.Stop()
for { for {
sb.log.Printf("Next update in %v", delay)
select { select {
case <-ticker.C: case <-time.After(delay):
sb.log.Printf("background threat list update") var ok bool
sb.c.Purge() if delay, ok = sb.db.Update(sb.api); ok {
sb.db.Update(sb.api) sb.log.Printf("background threat list updated")
sb.c.Purge()
}
case <-sb.done: case <-sb.done:
return return
} }

View File

@ -426,13 +426,9 @@ func generateLookupHosts(urlStr string) ([]string, error) {
return nil, err return nil, err
} }
// handle IPv4 and IPv6 addresses. // handle IPv4 and IPv6 addresses.
u, err := url.Parse(urlStr) ip := net.ParseIP(strings.Trim(host, "[]"))
if err != nil {
return nil, err
}
ip := net.ParseIP(strings.Trim(u.Host, "[]"))
if ip != nil { if ip != nil {
return []string{u.Host}, nil return []string{host}, nil
} }
hostComponents := strings.Split(host, ".") hostComponents := strings.Split(host, ".")