AudriusButkevicius 9 年 前
コミット
bbe0d34f43
22 ファイル変更1708 行追加0 行削除
  1. 10 0
      cmd/strelaypoolsrv/Godeps/Godeps.json
  2. 3 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.gitmodules
  3. 20 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.travis.yml
  4. 13 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/LICENSE
  5. 90 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/README.md
  6. 202 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/reader.go
  7. 3 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.gitmodules
  8. 23 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.travis.yml
  9. 13 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/LICENSE
  10. 37 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/README.md
  11. 19 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/appveyor.yml
  12. 530 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/decoder.go
  13. 17 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/errors.go
  14. 7 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_appengine.go
  15. 28 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_other.go
  16. 15 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_unix.go
  17. 82 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_windows.go
  18. 221 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader.go
  19. 26 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_appengine.go
  20. 56 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_other.go
  21. 108 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/traverse.go
  22. 185 0
      cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/verifier.go

+ 10 - 0
cmd/strelaypoolsrv/Godeps/Godeps.json

@@ -29,6 +29,16 @@
 			"ImportPath": "github.com/juju/ratelimit",
 			"Rev": "772f5c38e468398c4511514f4f6aa9a4185bc0a0"
 		},
+		{
+			"ImportPath": "github.com/oschwald/geoip2-golang",
+			"Comment": "v0.1.0-28-gfe8132a",
+			"Rev": "fe8132a35233b371398441ab4c5655ae1244564a"
+		},
+		{
+			"ImportPath": "github.com/oschwald/maxminddb-golang",
+			"Comment": "v0.2.0-41-gcf814d2",
+			"Rev": "cf814d2e9ee3d6ef5b756c0696548eb2f5508e03"
+		},
 		{
 			"ImportPath": "github.com/syncthing/syncthing/lib/dialer",
 			"Comment": "v0.12.6-6-g38e9b92",

+ 3 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.gitmodules

@@ -0,0 +1,3 @@
+[submodule "test-data"]
+	path = test-data
+	url = git://github.com/maxmind/MaxMind-DB.git

+ 20 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.travis.yml

@@ -0,0 +1,20 @@
+language: go
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+before_install:
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then go get -v github.com/golang/lint/golint; fi"
+
+script:
+  - go test -race -cpu 1,4 -v
+  - go test -race -v -tags appengine
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then go vet ./...; fi"
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then golint .; fi"
+
+sudo: false

+ 13 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/LICENSE

@@ -0,0 +1,13 @@
+Copyright (c) 2015, Gregory J. Oschwald <[email protected]>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.

+ 90 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/README.md

@@ -0,0 +1,90 @@
+# GeoIP2 Reader for Go #
+
+[![Build Status](https://travis-ci.org/oschwald/geoip2-golang.png?branch=master)](https://travis-ci.org/oschwald/geoip2-golang)
+[![GoDoc](https://godoc.org/github.com/oschwald/geoip2-golang?status.png)](https://godoc.org/github.com/oschwald/geoip2-golang)
+
+This library reads MaxMind [GeoLite2](http://dev.maxmind.com/geoip/geoip2/geolite2/)
+and [GeoIP2](http://www.maxmind.com/en/geolocation_landing) databases.
+
+This library is built using
+[the Go maxminddb reader](https://github.com/oschwald/maxminddb-golang).
+All data for the database record is decoded using this library. If you only
+need several fields, you may get superior performance by using maxminddb's
+`Lookup` directly with a result struct that only contains the required fields.
+(See [example_test.go](https://github.com/oschwald/maxminddb-golang/blob/master/example_test.go)
+in the maxminddb repository for an example of this.)
+
+## Installation ##
+
+```
+go get github.com/oschwald/geoip2-golang
+```
+
+## Usage ##
+
+[See GoDoc](http://godoc.org/github.com/oschwald/geoip2-golang) for
+documentation and examples.
+
+## Example ##
+
+```go
+package main
+
+import (
+    "fmt"
+    "github.com/oschwald/geoip2-golang"
+    "log"
+    "net"
+)
+
+func main() {
+    db, err := geoip2.Open("GeoIP2-City.mmdb")
+    if err != nil {
+            log.Fatal(err)
+    }
+    defer db.Close()
+    // If you are using strings that may be invalid, check that ip is not nil
+    ip := net.ParseIP("81.2.69.142")
+    record, err := db.City(ip)
+    if err != nil {
+            log.Fatal(err)
+    }
+    fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"])
+    fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"])
+    fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"])
+    fmt.Printf("ISO country code: %v\n", record.Country.IsoCode)
+    fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
+    fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
+    // Output:
+    // Portuguese (BR) city name: Londres
+    // English subdivision name: England
+    // Russian country name: Великобритания
+    // ISO country code: GB
+    // Time zone: Europe/London
+    // Coordinates: 51.5142, -0.0931
+}
+```
+
+## Testing ##
+
+Make sure you checked out test data submodule:
+
+```
+git submodule init
+git submodule update
+```
+
+Execute test suite:
+
+```
+go test
+```
+
+## Contributing ##
+
+Contributions welcome! Please fork the repository and open a pull request
+with your changes.
+
+## License ##
+
+This is free software, licensed under the ISC license.

+ 202 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/reader.go

@@ -0,0 +1,202 @@
+// Package geoip2 provides a wrapper around the maxminddb package for
+// easy use with the MaxMind GeoIP2 and GeoLite2 databases. The records for
+// the IP address is returned from this package as well-formed structures
+// that match the internal layout of data from MaxMind.
+package geoip2
+
+import (
+	"net"
+
+	"github.com/oschwald/maxminddb-golang"
+)
+
+// The City structure corresponds to the data in the GeoIP2/GeoLite2 City
+// databases.
+type City struct {
+	City struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"city"`
+	Continent struct {
+		Code      string            `maxminddb:"code"`
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"continent"`
+	Country struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"country"`
+	Location struct {
+		Latitude  float64 `maxminddb:"latitude"`
+		Longitude float64 `maxminddb:"longitude"`
+		MetroCode uint    `maxminddb:"metro_code"`
+		TimeZone  string  `maxminddb:"time_zone"`
+	} `maxminddb:"location"`
+	Postal struct {
+		Code string `maxminddb:"code"`
+	} `maxminddb:"postal"`
+	RegisteredCountry struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"registered_country"`
+	RepresentedCountry struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+		Type      string            `maxminddb:"type"`
+	} `maxminddb:"represented_country"`
+	Subdivisions []struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"subdivisions"`
+	Traits struct {
+		IsAnonymousProxy    bool `maxminddb:"is_anonymous_proxy"`
+		IsSatelliteProvider bool `maxminddb:"is_satellite_provider"`
+	} `maxminddb:"traits"`
+}
+
+// The Country structure corresponds to the data in the GeoIP2/GeoLite2
+// Country databases.
+type Country struct {
+	Continent struct {
+		Code      string            `maxminddb:"code"`
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"continent"`
+	Country struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"country"`
+	RegisteredCountry struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+	} `maxminddb:"registered_country"`
+	RepresentedCountry struct {
+		GeoNameID uint              `maxminddb:"geoname_id"`
+		IsoCode   string            `maxminddb:"iso_code"`
+		Names     map[string]string `maxminddb:"names"`
+		Type      string            `maxminddb:"type"`
+	} `maxminddb:"represented_country"`
+	Traits struct {
+		IsAnonymousProxy    bool `maxminddb:"is_anonymous_proxy"`
+		IsSatelliteProvider bool `maxminddb:"is_satellite_provider"`
+	} `maxminddb:"traits"`
+}
+
+// The AnonymousIP structure corresponds to the data in the GeoIP2
+// Anonymous IP database.
+type AnonymousIP struct {
+	IsAnonymous       bool `maxminddb:"is_anonymous"`
+	IsAnonymousVPN    bool `maxminddb:"is_anonymous_vpn"`
+	IsHostingProvider bool `maxminddb:"is_hosting_provider"`
+	IsPublicProxy     bool `maxminddb:"is_public_proxy"`
+	IsTorExitNode     bool `maxminddb:"is_tor_exit_node"`
+}
+
+// The ConnectionType structure corresponds to the data in the GeoIP2
+// Connection-Type database.
+type ConnectionType struct {
+	ConnectionType string `maxminddb:"connection_type"`
+}
+
+// The Domain structure corresponds to the data in the GeoIP2 Domain database.
+type Domain struct {
+	Domain string `maxminddb:"domain"`
+}
+
+// The ISP structure corresponds to the data in the GeoIP2 ISP database.
+type ISP struct {
+	AutonomousSystemNumber       uint   `maxminddb:"autonomous_system_number"`
+	AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
+	ISP                          string `maxminddb:"isp"`
+	Organization                 string `maxminddb:"organization"`
+}
+
+// Reader holds the maxminddb.Reader structure. It should be created
+// using the Open function.
+type Reader struct {
+	mmdbReader *maxminddb.Reader
+}
+
+// Open takes a string path to a file and returns a Reader structure or an
+// error. The database file is opened using a memory map. Use the Close method
+// on the Reader object to return the resources to the system.
+func Open(file string) (*Reader, error) {
+	reader, err := maxminddb.Open(file)
+	return &Reader{mmdbReader: reader}, err
+}
+
+// FromBytes takes a byte slice corresponding to a GeoIP2/GeoLite2 database
+// file and returns a Reader structure or an error.
+func FromBytes(bytes []byte) (*Reader, error) {
+	reader, err := maxminddb.FromBytes(bytes)
+	return &Reader{mmdbReader: reader}, err
+}
+
+// City takes an IP address as a net.IP struct and returns a City struct
+// and/or an error. Although this can be used with other databases, this
+// method generally should be used with the GeoIP2 or GeoLite2 City databases.
+func (r *Reader) City(ipAddress net.IP) (*City, error) {
+	var city City
+	err := r.mmdbReader.Lookup(ipAddress, &city)
+	return &city, err
+}
+
+// Country takes an IP address as a net.IP struct and returns a Country struct
+// and/or an error. Although this can be used with other databases, this
+// method generally should be used with the GeoIP2 or GeoLite2 Country
+// databases.
+func (r *Reader) Country(ipAddress net.IP) (*Country, error) {
+	var country Country
+	err := r.mmdbReader.Lookup(ipAddress, &country)
+	return &country, err
+}
+
+// AnonymousIP takes an IP address as a net.IP struct and returns a
+// AnonymousIP struct and/or an error.
+func (r *Reader) AnonymousIP(ipAddress net.IP) (*AnonymousIP, error) {
+	var anonIP AnonymousIP
+	err := r.mmdbReader.Lookup(ipAddress, &anonIP)
+	return &anonIP, err
+}
+
+// ConnectionType takes an IP address as a net.IP struct and returns a
+// ConnectionType struct and/or an error
+func (r *Reader) ConnectionType(ipAddress net.IP) (*ConnectionType, error) {
+	var val ConnectionType
+	err := r.mmdbReader.Lookup(ipAddress, &val)
+	return &val, err
+}
+
+// Domain takes an IP address as a net.IP struct and returns a
+// Domain struct and/or an error
+func (r *Reader) Domain(ipAddress net.IP) (*Domain, error) {
+	var val Domain
+	err := r.mmdbReader.Lookup(ipAddress, &val)
+	return &val, err
+}
+
+// ISP takes an IP address as a net.IP struct and returns a ISP struct and/or
+// an error
+func (r *Reader) ISP(ipAddress net.IP) (*ISP, error) {
+	var val ISP
+	err := r.mmdbReader.Lookup(ipAddress, &val)
+	return &val, err
+}
+
+// Metadata takes no arguments and returns a struct containing metadata about
+// the MaxMind database in use by the Reader.
+func (r *Reader) Metadata() maxminddb.Metadata {
+	return r.mmdbReader.Metadata
+}
+
+// Close unmaps the database file from virtual memory and returns the
+// resources to the system.
+func (r *Reader) Close() error {
+	return r.mmdbReader.Close()
+}

+ 3 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.gitmodules

@@ -0,0 +1,3 @@
+[submodule "test-data"]
+	path = test-data
+	url = git://github.com/maxmind/MaxMind-DB.git

+ 23 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.travis.yml

@@ -0,0 +1,23 @@
+language: go
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+before_install:
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then go get -v github.com/golang/lint/golint; fi"
+
+install:
+  - go get gopkg.in/check.v1
+
+script:
+  - go test -race -cpu 1,4 -v
+  - go test -race -v -tags appengine
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then go vet ./...; fi"
+  - "if [[ $TRAVIS_GO_VERSION == 1.6 ]]; then golint .; fi"
+
+sudo: false

+ 13 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/LICENSE

@@ -0,0 +1,13 @@
+Copyright (c) 2015, Gregory J. Oschwald <[email protected]>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.

+ 37 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/README.md

@@ -0,0 +1,37 @@
+# MaxMind DB Reader for Go #
+
+[![Build Status](https://travis-ci.org/oschwald/maxminddb-golang.png?branch=master)](https://travis-ci.org/oschwald/maxminddb-golang)
+[![Windows Build Status](https://ci.appveyor.com/api/projects/status/4j2f9oep8nnfrmov/branch/master?svg=true)](https://ci.appveyor.com/project/oschwald/maxminddb-golang/branch/master)
+[![GoDoc](https://godoc.org/github.com/oschwald/maxminddb-golang?status.png)](https://godoc.org/github.com/oschwald/maxminddb-golang)
+
+
+This is a Go reader for the MaxMind DB format. This can be used to read
+[GeoLite2](http://dev.maxmind.com/geoip/geoip2/geolite2/) and
+[GeoIP2](http://www.maxmind.com/en/geolocation_landing) databases.
+
+This is not an official MaxMind API.
+
+## Installation ##
+
+```
+go get github.com/oschwald/maxminddb-golang
+```
+
+## Usage ##
+
+[See GoDoc](http://godoc.org/github.com/oschwald/maxminddb-golang) for
+documentation and examples.
+
+## Examples ##
+
+See [GoDoc](http://godoc.org/github.com/oschwald/maxminddb-golang) or
+`example_test.go` for examples.
+
+## Contributing ##
+
+Contributions welcome! Please fork the repository and open a pull request
+with your changes.
+
+## License ##
+
+This is free software, licensed under the ISC License.

+ 19 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/appveyor.yml

@@ -0,0 +1,19 @@
+version: "{build}"
+
+os: Windows Server 2012 R2
+
+clone_folder: c:\gopath\src\github.com\oschwald\maxminddb-golang
+
+environment:
+  GOPATH: c:\gopath
+
+install:
+  - echo %PATH%
+  - echo %GOPATH%
+  - git submodule update --init --recursive
+  - go version
+  - go env
+  - go get -v -t ./...
+
+build_script:
+  - go test -v ./...

+ 530 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/decoder.go

@@ -0,0 +1,530 @@
+package maxminddb
+
+import (
+	"encoding/binary"
+	"fmt"
+	"math"
+	"math/big"
+	"reflect"
+	"sync"
+)
+
+type decoder struct {
+	buffer []byte
+}
+
+type dataType int
+
+const (
+	_Extended dataType = iota
+	_Pointer
+	_String
+	_Float64
+	_Bytes
+	_Uint16
+	_Uint32
+	_Map
+	_Int32
+	_Uint64
+	_Uint128
+	_Slice
+	_Container
+	_Marker
+	_Bool
+	_Float32
+)
+
+func (d *decoder) decode(offset uint, result reflect.Value) (uint, error) {
+	typeNum, size, newOffset := d.decodeCtrlData(offset)
+	return d.decodeFromType(typeNum, size, newOffset, result)
+}
+
+func (d *decoder) decodeCtrlData(offset uint) (dataType, uint, uint) {
+	newOffset := offset + 1
+	ctrlByte := d.buffer[offset]
+
+	typeNum := dataType(ctrlByte >> 5)
+	if typeNum == _Extended {
+		typeNum = dataType(d.buffer[newOffset] + 7)
+		newOffset++
+	}
+
+	var size uint
+	size, newOffset = d.sizeFromCtrlByte(ctrlByte, newOffset, typeNum)
+	return typeNum, size, newOffset
+}
+
+func (d *decoder) sizeFromCtrlByte(ctrlByte byte, offset uint, typeNum dataType) (uint, uint) {
+	size := uint(ctrlByte & 0x1f)
+	if typeNum == _Extended {
+		return size, offset
+	}
+
+	var bytesToRead uint
+	if size > 28 {
+		bytesToRead = size - 28
+	}
+
+	newOffset := offset + bytesToRead
+	sizeBytes := d.buffer[offset:newOffset]
+
+	switch {
+	case size == 29:
+		size = 29 + uint(sizeBytes[0])
+	case size == 30:
+		size = 285 + uint(uintFromBytes(0, sizeBytes))
+	case size > 30:
+		size = uint(uintFromBytes(0, sizeBytes)) + 65821
+	}
+	return size, newOffset
+}
+
+func (d *decoder) decodeFromType(dtype dataType, size uint, offset uint, result reflect.Value) (uint, error) {
+	if result.Kind() == reflect.Ptr {
+		result = reflect.Indirect(result)
+	}
+
+	switch dtype {
+	case _Bool:
+		return d.unmarshalBool(size, offset, result)
+	case _Bytes:
+		return d.unmarshalBytes(size, offset, result)
+	case _Float32:
+		return d.unmarshalFloat32(size, offset, result)
+	case _Float64:
+		return d.unmarshalFloat64(size, offset, result)
+	case _Int32:
+		return d.unmarshalInt32(size, offset, result)
+	case _Map:
+		return d.unmarshalMap(size, offset, result)
+	case _Pointer:
+		return d.unmarshalPointer(size, offset, result)
+	case _Slice:
+		return d.unmarshalSlice(size, offset, result)
+	case _String:
+		return d.unmarshalString(size, offset, result)
+	case _Uint16:
+		return d.unmarshalUint(size, offset, result, 16)
+	case _Uint32:
+		return d.unmarshalUint(size, offset, result, 32)
+	case _Uint64:
+		return d.unmarshalUint(size, offset, result, 64)
+	case _Uint128:
+		return d.unmarshalUint128(size, offset, result)
+	default:
+		return 0, newInvalidDatabaseError("unknown type: %d", dtype)
+	}
+}
+
+func (d *decoder) unmarshalBool(size uint, offset uint, result reflect.Value) (uint, error) {
+	if size > 1 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (bool size of %v)", size)
+	}
+	value, newOffset, err := d.decodeBool(size, offset)
+	if err != nil {
+		return 0, err
+	}
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Bool:
+		result.SetBool(value)
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalBytes(size uint, offset uint, result reflect.Value) (uint, error) {
+
+	value, newOffset, err := d.decodeBytes(size, offset)
+	if err != nil {
+		return 0, err
+	}
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Slice:
+		result.SetBytes(value)
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalFloat32(size uint, offset uint, result reflect.Value) (uint, error) {
+	if size != 4 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (float32 size of %v)", size)
+	}
+	value, newOffset, err := d.decodeFloat32(size, offset)
+	if err != nil {
+		return 0, err
+	}
+
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Float32, reflect.Float64:
+		result.SetFloat(float64(value))
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalFloat64(size uint, offset uint, result reflect.Value) (uint, error) {
+
+	if size != 8 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (float 64 size of %v)", size)
+	}
+	value, newOffset, err := d.decodeFloat64(size, offset)
+	if err != nil {
+		return 0, err
+	}
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Float32, reflect.Float64:
+		result.SetFloat(value)
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalInt32(size uint, offset uint, result reflect.Value) (uint, error) {
+	if size > 4 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (int32 size of %v)", size)
+	}
+	value, newOffset, err := d.decodeInt(size, offset)
+	if err != nil {
+		return 0, err
+	}
+
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		result.SetInt(int64(value))
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalMap(size uint, offset uint, result reflect.Value) (uint, error) {
+	switch result.Kind() {
+	default:
+		return 0, fmt.Errorf("trying to unmarshal a map into %v", result.Type())
+	case reflect.Struct:
+		return d.decodeStruct(size, offset, result)
+	case reflect.Map:
+		return d.decodeMap(size, offset, result)
+	case reflect.Interface:
+		rv := reflect.ValueOf(make(map[string]interface{}, size))
+		newOffset, err := d.decodeMap(size, offset, rv)
+		result.Set(rv)
+		return newOffset, err
+	}
+}
+
+func (d *decoder) unmarshalPointer(size uint, offset uint, result reflect.Value) (uint, error) {
+	pointer, newOffset := d.decodePointer(size, offset)
+	_, err := d.decode(pointer, result)
+	return newOffset, err
+}
+
+func (d *decoder) unmarshalSlice(size uint, offset uint, result reflect.Value) (uint, error) {
+
+	switch result.Kind() {
+	default:
+		return 0, fmt.Errorf("trying to unmarshal an array into %v", result.Type())
+	case reflect.Slice:
+		return d.decodeSlice(size, offset, result)
+	case reflect.Interface:
+		a := []interface{}{}
+		rv := reflect.ValueOf(&a).Elem()
+		newOffset, err := d.decodeSlice(size, offset, rv)
+		result.Set(rv)
+		return newOffset, err
+	}
+}
+
+func (d *decoder) unmarshalString(size uint, offset uint, result reflect.Value) (uint, error) {
+
+	value, newOffset, err := d.decodeString(size, offset)
+
+	if err != nil {
+		return 0, err
+	}
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.String:
+		result.SetString(value)
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalUint(size uint, offset uint, result reflect.Value, uintType uint) (uint, error) {
+	if size > uintType/8 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (uint%v size of %v)", uintType, size)
+	}
+
+	value, newOffset, err := d.decodeUint(size, offset)
+	if err != nil {
+		return 0, err
+	}
+
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		result.SetUint(value)
+		return newOffset, nil
+	case reflect.Interface:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) unmarshalUint128(size uint, offset uint, result reflect.Value) (uint, error) {
+	if size > 16 {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (uint128 size of %v)", size)
+	}
+	value, newOffset, err := d.decodeUint128(size, offset)
+	if err != nil {
+		return 0, err
+	}
+
+	// XXX - this should allow *big.Int rather than just bigInt
+	// Currently this is reported as invalid
+	switch result.Kind() {
+	default:
+		return newOffset, fmt.Errorf("trying to unmarshal %v into %v", value, result.Type())
+	case reflect.Struct:
+		result.Set(reflect.ValueOf(*value))
+		return newOffset, nil
+	case reflect.Interface, reflect.Ptr:
+		result.Set(reflect.ValueOf(value))
+		return newOffset, nil
+	}
+}
+
+func (d *decoder) decodeBool(size uint, offset uint) (bool, uint, error) {
+	return size != 0, offset, nil
+}
+
+func (d *decoder) decodeBytes(size uint, offset uint) ([]byte, uint, error) {
+	newOffset := offset + size
+	bytes := make([]byte, size)
+	copy(bytes, d.buffer[offset:newOffset])
+	return bytes, newOffset, nil
+}
+
+func (d *decoder) decodeFloat64(size uint, offset uint) (float64, uint, error) {
+	newOffset := offset + size
+	bits := binary.BigEndian.Uint64(d.buffer[offset:newOffset])
+	return math.Float64frombits(bits), newOffset, nil
+}
+
+func (d *decoder) decodeFloat32(size uint, offset uint) (float32, uint, error) {
+	newOffset := offset + size
+	bits := binary.BigEndian.Uint32(d.buffer[offset:newOffset])
+	return math.Float32frombits(bits), newOffset, nil
+}
+
+func (d *decoder) decodeInt(size uint, offset uint) (int, uint, error) {
+	newOffset := offset + size
+	var val int32
+	for _, b := range d.buffer[offset:newOffset] {
+		val = (val << 8) | int32(b)
+	}
+	return int(val), newOffset, nil
+}
+
+func (d *decoder) decodeMap(size uint, offset uint, result reflect.Value) (uint, error) {
+	if result.IsNil() {
+		result.Set(reflect.MakeMap(result.Type()))
+	}
+
+	for i := uint(0); i < size; i++ {
+		var key string
+		var err error
+		key, offset, err = d.decodeKeyString(offset)
+
+		if err != nil {
+			return 0, err
+		}
+
+		value := reflect.New(result.Type().Elem())
+		offset, err = d.decode(offset, value)
+		if err != nil {
+			return 0, err
+		}
+		result.SetMapIndex(reflect.ValueOf(key), value.Elem())
+	}
+	return offset, nil
+}
+
+func (d *decoder) decodePointer(size uint, offset uint) (uint, uint) {
+	pointerSize := ((size >> 3) & 0x3) + 1
+	newOffset := offset + pointerSize
+	pointerBytes := d.buffer[offset:newOffset]
+	var prefix uint64
+	if pointerSize == 4 {
+		prefix = 0
+	} else {
+		prefix = uint64(size & 0x7)
+	}
+	unpacked := uint(uintFromBytes(prefix, pointerBytes))
+
+	var pointerValueOffset uint
+	switch pointerSize {
+	case 1:
+		pointerValueOffset = 0
+	case 2:
+		pointerValueOffset = 2048
+	case 3:
+		pointerValueOffset = 526336
+	case 4:
+		pointerValueOffset = 0
+	}
+
+	pointer := unpacked + pointerValueOffset
+
+	return pointer, newOffset
+}
+
+func (d *decoder) decodeSlice(size uint, offset uint, result reflect.Value) (uint, error) {
+	result.Set(reflect.MakeSlice(result.Type(), int(size), int(size)))
+	for i := 0; i < int(size); i++ {
+		var err error
+		offset, err = d.decode(offset, result.Index(i))
+		if err != nil {
+			return 0, err
+		}
+	}
+	return offset, nil
+}
+
+func (d *decoder) decodeString(size uint, offset uint) (string, uint, error) {
+	newOffset := offset + size
+	return string(d.buffer[offset:newOffset]), newOffset, nil
+}
+
+var (
+	fieldMap   = map[reflect.Type]map[string]int{}
+	fieldMapMu sync.RWMutex
+)
+
+func (d *decoder) decodeStruct(size uint, offset uint, result reflect.Value) (uint, error) {
+	resultType := result.Type()
+
+	fieldMapMu.RLock()
+	fields, ok := fieldMap[resultType]
+	fieldMapMu.RUnlock()
+	if !ok {
+		numFields := resultType.NumField()
+		fields = make(map[string]int, numFields)
+		for i := 0; i < numFields; i++ {
+			fieldType := resultType.Field(i)
+
+			fieldName := fieldType.Name
+			if tag := fieldType.Tag.Get("maxminddb"); tag != "" {
+				fieldName = tag
+			}
+			fields[fieldName] = i
+		}
+		fieldMapMu.Lock()
+		fieldMap[resultType] = fields
+		fieldMapMu.Unlock()
+	}
+
+	for i := uint(0); i < size; i++ {
+		var (
+			err error
+			key string
+		)
+		key, offset, err = d.decodeStructKey(offset)
+		if err != nil {
+			return 0, err
+		}
+		i, ok := fields[key]
+		if !ok {
+			offset = d.nextValueOffset(offset, 1)
+			continue
+		}
+		offset, err = d.decode(offset, result.Field(i))
+		if err != nil {
+			return 0, err
+		}
+	}
+	return offset, nil
+}
+
+func (d *decoder) decodeUint(size uint, offset uint) (uint64, uint, error) {
+	newOffset := offset + size
+	val := uintFromBytes(0, d.buffer[offset:newOffset])
+
+	return val, newOffset, nil
+}
+
+func (d *decoder) decodeUint128(size uint, offset uint) (*big.Int, uint, error) {
+	newOffset := offset + size
+	val := new(big.Int)
+	val.SetBytes(d.buffer[offset:newOffset])
+
+	return val, newOffset, nil
+}
+
+func uintFromBytes(prefix uint64, uintBytes []byte) uint64 {
+	val := prefix
+	for _, b := range uintBytes {
+		val = (val << 8) | uint64(b)
+	}
+	return val
+}
+
+func (d *decoder) decodeKeyString(offset uint) (string, uint, error) {
+	typeNum, size, newOffset := d.decodeCtrlData(offset)
+	if typeNum == _Pointer {
+		pointer, ptrOffset := d.decodePointer(size, newOffset)
+		key, _, err := d.decodeKeyString(pointer)
+		return key, ptrOffset, err
+	}
+	if typeNum != _String {
+		return "", 0, newInvalidDatabaseError("unexpected type when decoding string: %v", typeNum)
+	}
+	return d.decodeString(size, newOffset)
+}
+
+// This function is used to skip ahead to the next value without decoding
+// the one at the offset passed in. The size bits have different meanings for
+// different data types
+func (d *decoder) nextValueOffset(offset uint, numberToSkip uint) uint {
+	if numberToSkip == 0 {
+		return offset
+	}
+	typeNum, size, offset := d.decodeCtrlData(offset)
+	switch typeNum {
+	case _Pointer:
+		_, offset = d.decodePointer(size, offset)
+	case _Map:
+		numberToSkip += 2 * size
+	case _Slice:
+		numberToSkip += size
+	case _Bool:
+	default:
+		offset += size
+	}
+	return d.nextValueOffset(offset, numberToSkip-1)
+}

+ 17 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/errors.go

@@ -0,0 +1,17 @@
+package maxminddb
+
+import "fmt"
+
+// InvalidDatabaseError is returned when the database contains invalid data
+// and cannot be parsed.
+type InvalidDatabaseError struct {
+	message string
+}
+
+func newInvalidDatabaseError(format string, args ...interface{}) InvalidDatabaseError {
+	return InvalidDatabaseError{fmt.Sprintf(format, args...)}
+}
+
+func (e InvalidDatabaseError) Error() string {
+	return e.message
+}

+ 7 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_appengine.go

@@ -0,0 +1,7 @@
+// +build appengine
+
+package maxminddb
+
+func (d *decoder) decodeStructKey(offset uint) (string, uint, error) {
+	return d.decodeKeyString(offset)
+}

+ 28 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_other.go

@@ -0,0 +1,28 @@
+// +build !appengine
+
+package maxminddb
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+// decodeStructKey returns a string which points into the database. Don't keep
+// it around.
+func (d *decoder) decodeStructKey(offset uint) (string, uint, error) {
+	typeNum, size, newOffset := d.decodeCtrlData(offset)
+	switch typeNum {
+	case _Pointer:
+		pointer, ptrOffset := d.decodePointer(size, newOffset)
+		s, _, err := d.decodeStructKey(pointer)
+		return s, ptrOffset, err
+	case _String:
+		var s string
+		val := (*reflect.StringHeader)(unsafe.Pointer(&s))
+		val.Data = uintptr(unsafe.Pointer(&d.buffer[newOffset]))
+		val.Len = int(size)
+		return s, newOffset + size, nil
+	default:
+		return "", 0, newInvalidDatabaseError("unexpected type when decoding struct key: %v", typeNum)
+	}
+}

+ 15 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_unix.go

@@ -0,0 +1,15 @@
+// +build !windows,!appengine
+
+package maxminddb
+
+import (
+	"syscall"
+)
+
+func mmap(fd int, length int) (data []byte, err error) {
+	return syscall.Mmap(fd, 0, length, syscall.PROT_READ, syscall.MAP_SHARED)
+}
+
+func munmap(b []byte) (err error) {
+	return syscall.Munmap(b)
+}

+ 82 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_windows.go

@@ -0,0 +1,82 @@
+package maxminddb
+
+// Windows support largely borrowed from mmap-go.
+//
+// Copyright 2011 Evan Shaw. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+import (
+	"errors"
+	"os"
+	"reflect"
+	"sync"
+	"syscall"
+	"unsafe"
+)
+
+type memoryMap []byte
+
+// Windows
+var handleLock sync.Mutex
+var handleMap = map[uintptr]syscall.Handle{}
+
+func mmap(fd int, length int) (data []byte, err error) {
+	h, errno := syscall.CreateFileMapping(syscall.Handle(fd), nil,
+		uint32(syscall.PAGE_READONLY), 0, uint32(length), nil)
+	if h == 0 {
+		return nil, os.NewSyscallError("CreateFileMapping", errno)
+	}
+
+	addr, errno := syscall.MapViewOfFile(h, uint32(syscall.FILE_MAP_READ), 0,
+		0, uintptr(length))
+	if addr == 0 {
+		return nil, os.NewSyscallError("MapViewOfFile", errno)
+	}
+	handleLock.Lock()
+	handleMap[addr] = h
+	handleLock.Unlock()
+
+	m := memoryMap{}
+	dh := m.header()
+	dh.Data = addr
+	dh.Len = length
+	dh.Cap = dh.Len
+
+	return m, nil
+}
+
+func (m *memoryMap) header() *reflect.SliceHeader {
+	return (*reflect.SliceHeader)(unsafe.Pointer(m))
+}
+
+func flush(addr, len uintptr) error {
+	errno := syscall.FlushViewOfFile(addr, len)
+	return os.NewSyscallError("FlushViewOfFile", errno)
+}
+
+func munmap(b []byte) (err error) {
+	m := memoryMap(b)
+	dh := m.header()
+
+	addr := dh.Data
+	length := uintptr(dh.Len)
+
+	flush(addr, length)
+	err = syscall.UnmapViewOfFile(addr)
+	if err != nil {
+		return err
+	}
+
+	handleLock.Lock()
+	defer handleLock.Unlock()
+	handle, ok := handleMap[addr]
+	if !ok {
+		// should be impossible; we would've errored above
+		return errors.New("unknown base address")
+	}
+	delete(handleMap, addr)
+
+	e := syscall.CloseHandle(syscall.Handle(handle))
+	return os.NewSyscallError("CloseHandle", e)
+}

+ 221 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader.go

@@ -0,0 +1,221 @@
+package maxminddb
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"net"
+	"reflect"
+)
+
+const dataSectionSeparatorSize = 16
+
+var metadataStartMarker = []byte("\xAB\xCD\xEFMaxMind.com")
+
+// Reader holds the data corresponding to the MaxMind DB file. Its only public
+// field is Metadata, which contains the metadata from the MaxMind DB file.
+type Reader struct {
+	hasMappedFile bool
+	buffer        []byte
+	decoder       decoder
+	Metadata      Metadata
+	ipv4Start     uint
+}
+
+// Metadata holds the metadata decoded from the MaxMind DB file. In particular
+// in has the format version, the build time as Unix epoch time, the database
+// type and description, the IP version supported, and a slice of the natural
+// languages included.
+type Metadata struct {
+	BinaryFormatMajorVersion uint              `maxminddb:"binary_format_major_version"`
+	BinaryFormatMinorVersion uint              `maxminddb:"binary_format_minor_version"`
+	BuildEpoch               uint              `maxminddb:"build_epoch"`
+	DatabaseType             string            `maxminddb:"database_type"`
+	Description              map[string]string `maxminddb:"description"`
+	IPVersion                uint              `maxminddb:"ip_version"`
+	Languages                []string          `maxminddb:"languages"`
+	NodeCount                uint              `maxminddb:"node_count"`
+	RecordSize               uint              `maxminddb:"record_size"`
+}
+
+// FromBytes takes a byte slice corresponding to a MaxMind DB file and returns
+// a Reader structure or an error.
+func FromBytes(buffer []byte) (*Reader, error) {
+	metadataStart := bytes.LastIndex(buffer, metadataStartMarker)
+
+	if metadataStart == -1 {
+		return nil, newInvalidDatabaseError("error opening database: invalid MaxMind DB file")
+	}
+
+	metadataStart += len(metadataStartMarker)
+	metadataDecoder := decoder{buffer[metadataStart:]}
+
+	var metadata Metadata
+
+	rvMetdata := reflect.ValueOf(&metadata)
+	_, err := metadataDecoder.decode(0, rvMetdata)
+	if err != nil {
+		return nil, err
+	}
+
+	searchTreeSize := metadata.NodeCount * metadata.RecordSize / 4
+	dataSectionStart := searchTreeSize + dataSectionSeparatorSize
+	dataSectionEnd := uint(metadataStart - len(metadataStartMarker))
+	if dataSectionStart > dataSectionEnd {
+		return nil, newInvalidDatabaseError("the MaxMind DB contains invalid metadata")
+	}
+	d := decoder{
+		buffer[searchTreeSize+dataSectionSeparatorSize : metadataStart-len(metadataStartMarker)],
+	}
+
+	reader := &Reader{
+		buffer:    buffer,
+		decoder:   d,
+		Metadata:  metadata,
+		ipv4Start: 0,
+	}
+
+	reader.ipv4Start, err = reader.startNode()
+
+	return reader, err
+}
+
+func (r *Reader) startNode() (uint, error) {
+	if r.Metadata.IPVersion != 6 {
+		return 0, nil
+	}
+
+	nodeCount := r.Metadata.NodeCount
+
+	node := uint(0)
+	var err error
+	for i := 0; i < 96 && node < nodeCount; i++ {
+		node, err = r.readNode(node, 0)
+		if err != nil {
+			return 0, err
+		}
+	}
+	return node, err
+}
+
+// Lookup takes an IP address as a net.IP structure and a pointer to the
+// result value to decode into. The result value pointed to must be a data
+// value that corresponds to a record in the database. This may include a
+// struct representation of the data, a map capable of holding the data or an
+// empty interface{} value.
+//
+// If result is a pointer to a struct, the struct need not include a field
+// for every value that may be in the database. If a field is not present in
+// the structure, the decoder will not decode that field, reducing the time
+// required to decode the record.
+//
+// Currently the decoder expect most data types to correspond exactly (e.g.,
+// a uint64 database type must be decoded into a uint64 Go type). In the
+// future, this may be made more flexible.
+func (r *Reader) Lookup(ipAddress net.IP, result interface{}) error {
+	if ipAddress == nil {
+		return errors.New("ipAddress passed to Lookup cannot be nil")
+	}
+
+	ipV4Address := ipAddress.To4()
+	if ipV4Address != nil {
+		ipAddress = ipV4Address
+	}
+	if len(ipAddress) == 16 && r.Metadata.IPVersion == 4 {
+		return fmt.Errorf("error looking up '%s': you attempted to look up an IPv6 address in an IPv4-only database", ipAddress.String())
+	}
+
+	pointer, err := r.findAddressInTree(ipAddress)
+
+	if pointer == 0 {
+		return err
+	}
+
+	return r.retrieveData(pointer, result)
+}
+
+func (r *Reader) findAddressInTree(ipAddress net.IP) (uint, error) {
+
+	bitCount := uint(len(ipAddress) * 8)
+
+	var node uint
+	if bitCount == 32 {
+		node = r.ipv4Start
+	}
+
+	nodeCount := r.Metadata.NodeCount
+
+	for i := uint(0); i < bitCount && node < nodeCount; i++ {
+		bit := uint(1) & (uint(ipAddress[i>>3]) >> (7 - (i % 8)))
+
+		var err error
+		node, err = r.readNode(node, bit)
+		if err != nil {
+			return 0, err
+		}
+	}
+	if node == nodeCount {
+		// Record is empty
+		return 0, nil
+	} else if node > nodeCount {
+		return node, nil
+	}
+
+	return 0, newInvalidDatabaseError("invalid node in search tree")
+}
+
+func (r *Reader) readNode(nodeNumber uint, index uint) (uint, error) {
+	RecordSize := r.Metadata.RecordSize
+
+	baseOffset := nodeNumber * RecordSize / 4
+
+	var nodeBytes []byte
+	var prefix uint64
+	switch RecordSize {
+	case 24:
+		offset := baseOffset + index*3
+		nodeBytes = r.buffer[offset : offset+3]
+	case 28:
+		prefix = uint64(r.buffer[baseOffset+3])
+		if index != 0 {
+			prefix &= 0x0F
+		} else {
+			prefix = (0xF0 & prefix) >> 4
+		}
+		offset := baseOffset + index*4
+		nodeBytes = r.buffer[offset : offset+3]
+	case 32:
+		offset := baseOffset + index*4
+		nodeBytes = r.buffer[offset : offset+4]
+	default:
+		return 0, newInvalidDatabaseError("unknown record size: %d", RecordSize)
+	}
+	return uint(uintFromBytes(prefix, nodeBytes)), nil
+}
+
+func (r *Reader) retrieveData(pointer uint, result interface{}) error {
+	rv := reflect.ValueOf(result)
+	if rv.Kind() != reflect.Ptr || rv.IsNil() {
+		return errors.New("result param must be a pointer")
+	}
+
+	offset, err := r.resolveDataPointer(pointer)
+	if err != nil {
+		return err
+	}
+
+	_, err = r.decoder.decode(offset, rv)
+	return err
+}
+
+func (r *Reader) resolveDataPointer(pointer uint) (uint, error) {
+	nodeCount := r.Metadata.NodeCount
+
+	resolved := pointer - nodeCount - dataSectionSeparatorSize
+
+	if resolved > uint(len(r.buffer)) {
+		return 0, newInvalidDatabaseError("the MaxMind DB file's search tree is corrupt")
+	}
+
+	return resolved, nil
+}

+ 26 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_appengine.go

@@ -0,0 +1,26 @@
+// +build appengine
+
+package maxminddb
+
+import "io/ioutil"
+
+// Open takes a string path to a MaxMind DB file and returns a Reader
+// structure or an error. The database file is opened using a memory map,
+// except on Google App Engine where mmap is not supported; there the database
+// is loaded into memory. Use the Close method on the Reader object to return
+// the resources to the system.
+func Open(file string) (*Reader, error) {
+	bytes, err := ioutil.ReadFile(file)
+	if err != nil {
+		return nil, err
+	}
+
+	return FromBytes(bytes)
+}
+
+// Close unmaps the database file from virtual memory and returns the
+// resources to the system. If called on a Reader opened using FromBytes
+// or Open on Google App Engine, this method does nothing.
+func (r *Reader) Close() error {
+	return nil
+}

+ 56 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_other.go

@@ -0,0 +1,56 @@
+// +build !appengine
+
+package maxminddb
+
+import "os"
+
+// Open takes a string path to a MaxMind DB file and returns a Reader
+// structure or an error. The database file is opened using a memory map,
+// except on Google App Engine where mmap is not supported; there the database
+// is loaded into memory. Use the Close method on the Reader object to return
+// the resources to the system.
+func Open(file string) (*Reader, error) {
+	mapFile, err := os.Open(file)
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		if rerr := mapFile.Close(); rerr != nil {
+			err = rerr
+		}
+	}()
+
+	stats, err := mapFile.Stat()
+	if err != nil {
+		return nil, err
+	}
+
+	fileSize := int(stats.Size())
+	mmap, err := mmap(int(mapFile.Fd()), fileSize)
+	if err != nil {
+		return nil, err
+	}
+
+	reader, err := FromBytes(mmap)
+	if err != nil {
+		if err2 := munmap(mmap); err2 != nil {
+			// failing to unmap the file is probably the more severe error
+			return nil, err2
+		}
+		return nil, err
+	}
+
+	reader.hasMappedFile = true
+	return reader, err
+}
+
+// Close unmaps the database file from virtual memory and returns the
+// resources to the system. If called on a Reader opened using FromBytes
+// or Open on Google App Engine, this method does nothing.
+func (r *Reader) Close() (err error) {
+	if r.hasMappedFile {
+		err = munmap(r.buffer)
+		r.hasMappedFile = false
+	}
+	return err
+}

+ 108 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/traverse.go

@@ -0,0 +1,108 @@
+package maxminddb
+
+import "net"
+
+// Internal structure used to keep track of nodes we still need to visit.
+type netNode struct {
+	ip      net.IP
+	bit     uint
+	pointer uint
+}
+
+// Networks represents a set of subnets that we are iterating over.
+type Networks struct {
+	reader   *Reader
+	nodes    []netNode // Nodes we still have to visit.
+	lastNode netNode
+	err      error
+}
+
+// Networks returns an iterator that can be used to traverse all networks in
+// the database.
+//
+// Please note that a MaxMind DB may map IPv4 networks into several locations
+// in in an IPv6 database. This iterator will iterate over all of these
+// locations separately.
+func (r *Reader) Networks() *Networks {
+	s := 4
+	if r.Metadata.IPVersion == 6 {
+		s = 16
+	}
+	return &Networks{
+		reader: r,
+		nodes: []netNode{
+			netNode{
+				ip: make(net.IP, s),
+			},
+		},
+	}
+}
+
+// Next prepares the next network for reading with the Network method. It
+// returns true if there is another network to be processed and false if there
+// are no more networks or if there is an error.
+func (n *Networks) Next() bool {
+	for len(n.nodes) > 0 {
+		node := n.nodes[len(n.nodes)-1]
+		n.nodes = n.nodes[:len(n.nodes)-1]
+
+		for {
+			if node.pointer < n.reader.Metadata.NodeCount {
+				ipRight := make(net.IP, len(node.ip))
+				copy(ipRight, node.ip)
+				if len(ipRight) <= int(node.bit>>3) {
+					n.err = newInvalidDatabaseError(
+						"invalid search tree at %v/%v", ipRight, node.bit)
+					return false
+				}
+				ipRight[node.bit>>3] |= 1 << uint(7-(node.bit%8))
+
+				rightPointer, err := n.reader.readNode(node.pointer, 1)
+				if err != nil {
+					n.err = err
+					return false
+				}
+
+				node.bit++
+				n.nodes = append(n.nodes, netNode{
+					pointer: rightPointer,
+					ip:      ipRight,
+					bit:     node.bit,
+				})
+
+				node.pointer, err = n.reader.readNode(node.pointer, 0)
+				if err != nil {
+					n.err = err
+					return false
+				}
+
+			} else if node.pointer > n.reader.Metadata.NodeCount {
+				n.lastNode = node
+				return true
+			} else {
+				break
+			}
+		}
+	}
+
+	return false
+}
+
+// Network returns the current network or an error if there is a problem
+// decoding the data for the network. It takes a pointer to a result value to
+// decode the network's data into.
+func (n *Networks) Network(result interface{}) (*net.IPNet, error) {
+	if err := n.reader.retrieveData(n.lastNode.pointer, result); err != nil {
+		return nil, err
+	}
+
+	return &net.IPNet{
+		IP:   n.lastNode.ip,
+		Mask: net.CIDRMask(int(n.lastNode.bit), len(n.lastNode.ip)*8),
+	}, nil
+}
+
+// Err returns an error, if any, that was encountered during iteration.
+func (n *Networks) Err() error {
+	return n.err
+}

+ 185 - 0
cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/verifier.go

@@ -0,0 +1,185 @@
+package maxminddb
+
+import "reflect"
+
+type verifier struct {
+	reader *Reader
+}
+
+// Verify checks that the database is valid. It validates the search tree,
+// the data section, and the metadata section. This verifier is stricter than
+// the specification and may return errors on databases that are readable.
+func (r *Reader) Verify() error {
+	v := verifier{r}
+	if err := v.verifyMetadata(); err != nil {
+		return err
+	}
+
+	return v.verifyDatabase()
+}
+
+func (v *verifier) verifyMetadata() error {
+	metadata := v.reader.Metadata
+
+	if metadata.BinaryFormatMajorVersion != 2 {
+		return testError(
+			"binary_format_major_version",
+			2,
+			metadata.BinaryFormatMajorVersion,
+		)
+	}
+
+	if metadata.BinaryFormatMinorVersion != 0 {
+		return testError(
+			"binary_format_minor_version",
+			0,
+			metadata.BinaryFormatMinorVersion,
+		)
+	}
+
+	if metadata.DatabaseType == "" {
+		return testError(
+			"database_type",
+			"non-empty string",
+			metadata.DatabaseType,
+		)
+	}
+
+	if len(metadata.Description) == 0 {
+		return testError(
+			"description",
+			"non-empty slice",
+			metadata.Description,
+		)
+	}
+
+	if metadata.IPVersion != 4 && metadata.IPVersion != 6 {
+		return testError(
+			"ip_version",
+			"4 or 6",
+			metadata.IPVersion,
+		)
+	}
+
+	if metadata.RecordSize != 24 &&
+		metadata.RecordSize != 28 &&
+		metadata.RecordSize != 32 {
+		return testError(
+			"record_size",
+			"24, 28, or 32",
+			metadata.RecordSize,
+		)
+	}
+
+	if metadata.NodeCount == 0 {
+		return testError(
+			"node_count",
+			"positive integer",
+			metadata.NodeCount,
+		)
+	}
+	return nil
+}
+
+func (v *verifier) verifyDatabase() error {
+	offsets, err := v.verifySearchTree()
+	if err != nil {
+		return err
+	}
+
+	if err := v.verifyDataSectionSeparator(); err != nil {
+		return err
+	}
+
+	return v.verifyDataSection(offsets)
+}
+
+func (v *verifier) verifySearchTree() (map[uint]bool, error) {
+	offsets := make(map[uint]bool)
+
+	it := v.reader.Networks()
+	for it.Next() {
+		offset, err := v.reader.resolveDataPointer(it.lastNode.pointer)
+		if err != nil {
+			return nil, err
+		}
+		offsets[offset] = true
+	}
+	if err := it.Err(); err != nil {
+		return nil, err
+	}
+	return offsets, nil
+}
+
+func (v *verifier) verifyDataSectionSeparator() error {
+	separatorStart := v.reader.Metadata.NodeCount * v.reader.Metadata.RecordSize / 4
+
+	separator := v.reader.buffer[separatorStart : separatorStart+dataSectionSeparatorSize]
+
+	for _, b := range separator {
+		if b != 0 {
+			return newInvalidDatabaseError("unexpected byte in data separator: %v", separator)
+		}
+	}
+	return nil
+}
+
+func (v *verifier) verifyDataSection(offsets map[uint]bool) error {
+	pointerCount := len(offsets)
+
+	decoder := v.reader.decoder
+
+	var offset uint
+	bufferLen := uint(len(decoder.buffer))
+	for offset < bufferLen {
+		var data interface{}
+		rv := reflect.ValueOf(&data)
+		newOffset, err := decoder.decode(offset, rv)
+		if err != nil {
+			return newInvalidDatabaseError("received decoding error (%v) at offset of %v", err, offset)
+		}
+		if newOffset <= offset {
+			return newInvalidDatabaseError("data section offset unexpectedly went from %v to %v", offset, newOffset)
+		}
+
+		pointer := offset
+
+		if _, ok := offsets[pointer]; ok {
+			delete(offsets, pointer)
+		} else {
+			return newInvalidDatabaseError("found data (%v) at %v that the search tree does not point to", data, pointer)
+		}
+
+		offset = newOffset
+	}
+
+	if offset != bufferLen {
+		return newInvalidDatabaseError(
+			"unexpected data at the end of the data section (last offset: %v, end: %v)",
+			offset,
+			bufferLen,
+		)
+	}
+
+	if len(offsets) != 0 {
+		return newInvalidDatabaseError(
+			"found %v pointers (of %v) in the search tree that we did not see in the data section",
+			len(offsets),
+			pointerCount,
+		)
+	}
+	return nil
+}
+
+func testError(
+	field string,
+	expected interface{},
+	actual interface{},
+) error {
+	return newInvalidDatabaseError(
+		"%v - Expected: %v Actual: %v",
+		field,
+		expected,
+		actual,
+	)
+}