diff --git a/cmd/strelaypoolsrv/Godeps/Godeps.json b/cmd/strelaypoolsrv/Godeps/Godeps.json index ce528cfdb..80cfa7c4f 100644 --- a/cmd/strelaypoolsrv/Godeps/Godeps.json +++ b/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", diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.gitmodules b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.gitmodules new file mode 100644 index 000000000..51779cbff --- /dev/null +++ b/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 diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.travis.yml b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/.travis.yml new file mode 100644 index 000000000..d2f256746 --- /dev/null +++ b/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 diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/LICENSE b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/LICENSE new file mode 100644 index 000000000..ba53dd189 --- /dev/null +++ b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Gregory J. Oschwald + +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. diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/README.md b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/README.md new file mode 100644 index 000000000..7a3c22a0a --- /dev/null +++ b/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. diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/reader.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/geoip2-golang/reader.go new file mode 100644 index 000000000..45d642bee --- /dev/null +++ b/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() +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.gitmodules b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.gitmodules new file mode 100644 index 000000000..51779cbff --- /dev/null +++ b/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 diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.travis.yml b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/.travis.yml new file mode 100644 index 000000000..e01d99a2a --- /dev/null +++ b/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 diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/LICENSE b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/LICENSE new file mode 100644 index 000000000..ba53dd189 --- /dev/null +++ b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Gregory J. Oschwald + +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. diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/README.md b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/README.md new file mode 100644 index 000000000..d83a2760a --- /dev/null +++ b/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. diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/appveyor.yml b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/appveyor.yml new file mode 100644 index 000000000..e2bb9dd23 --- /dev/null +++ b/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 ./... diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/decoder.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/decoder.go new file mode 100644 index 000000000..1782a36e7 --- /dev/null +++ b/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) +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/errors.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/errors.go new file mode 100644 index 000000000..2a2acf999 --- /dev/null +++ b/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 +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_appengine.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_appengine.go new file mode 100644 index 000000000..c7dc6ef07 --- /dev/null +++ b/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) +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_other.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/key_other.go new file mode 100644 index 000000000..577e6f801 --- /dev/null +++ b/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) + } +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_unix.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_unix.go new file mode 100644 index 000000000..835bc8f00 --- /dev/null +++ b/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) +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_windows.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/mmap_windows.go new file mode 100644 index 000000000..06bb99845 --- /dev/null +++ b/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) +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader.go new file mode 100644 index 000000000..a65abde11 --- /dev/null +++ b/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 +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_appengine.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_appengine.go new file mode 100644 index 000000000..631e19532 --- /dev/null +++ b/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 +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_other.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_other.go new file mode 100644 index 000000000..0ef5df3a9 --- /dev/null +++ b/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 +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/traverse.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/traverse.go new file mode 100644 index 000000000..e794d9982 --- /dev/null +++ b/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 +} diff --git a/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/verifier.go b/cmd/strelaypoolsrv/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/verifier.go new file mode 100644 index 000000000..8c109c459 --- /dev/null +++ b/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, + ) +}